httpsvrkit.h 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. //
  2. // httpsvrkit.h
  3. //
  4. // Copyright (c) 2012 Yuji Hirose. All rights reserved.
  5. // The Boost Software License 1.0
  6. //
  7. #ifdef _WIN32
  8. //#define _CRT_SECURE_NO_WARNINGS
  9. #define _CRT_NONSTDC_NO_DEPRECATE
  10. #include <fcntl.h>
  11. #include <io.h>
  12. #include <winsock2.h>
  13. typedef SOCKET socket_t;
  14. #else
  15. #include <pthread.h>
  16. #include <unistd.h>
  17. #include <netdb.h>
  18. #include <netinet/in.h>
  19. #include <arpa/inet.h>
  20. #include <sys/socket.h>
  21. typedef int socket_t;
  22. #endif
  23. #include <functional>
  24. #include <map>
  25. #include <regex>
  26. #include <string>
  27. #include <assert.h>
  28. namespace httpsvrkit
  29. {
  30. typedef std::map<std::string, std::string> Map;
  31. typedef std::multimap<std::string, std::string> MultiMap;
  32. // HTTP request
  33. struct Request {
  34. Map headers;
  35. std::string body;
  36. std::string pattern;
  37. Map params;
  38. };
  39. // HTTP response
  40. struct Response {
  41. MultiMap headers;
  42. std::string body;
  43. };
  44. struct Context {
  45. const Request request;
  46. Response response;
  47. };
  48. // HTTP server
  49. class Server {
  50. public:
  51. typedef std::function<void (Context& context)> Handler;
  52. Server();
  53. ~Server();
  54. void get(const char* pattern, Handler handler);
  55. void post(const char* pattern, Handler handler);
  56. bool run(const char* ipaddr_or_hostname, int port);
  57. void stop();
  58. private:
  59. void process_request(FILE* fp_read, FILE* fp_write);
  60. socket_t sock_;
  61. std::multimap<std::string, Handler> handlers_;
  62. };
  63. // Implementation
  64. inline socket_t create_server_socket(const const char* ipaddr_or_hostname, int port)
  65. {
  66. // Create a server socket
  67. socket_t sock = socket(AF_INET, SOCK_STREAM, 0);
  68. if (sock == -1) {
  69. return -1;
  70. }
  71. // Make 'reuse address' option available
  72. int opt = 1;
  73. setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
  74. // Get a host entry info
  75. struct hostent* hp;
  76. if (!(hp = gethostbyname(ipaddr_or_hostname))) {
  77. return -1;
  78. }
  79. // Bind the socket to the given address
  80. struct sockaddr_in addr;
  81. memset(&addr, 0, sizeof(addr));
  82. memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
  83. addr.sin_family = AF_INET;
  84. addr.sin_port = htons(port);
  85. if (::bind(sock, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
  86. puts("(error)\n");
  87. return -1;
  88. }
  89. // Listen through 5 channels
  90. if (listen(sock, 5) != 0) {
  91. return -1;
  92. }
  93. return sock;
  94. }
  95. inline void close_socket(socket_t sock)
  96. {
  97. #ifdef _WIN32
  98. closesocket(sock);
  99. #else
  100. shutdown(sock, SHUT_RDWR);
  101. close(sock);
  102. #endif
  103. }
  104. inline Server::Server()
  105. : sock_(-1)
  106. {
  107. #ifdef _WIN32
  108. WSADATA wsaData;
  109. WSAStartup(0x0002, &wsaData);
  110. #ifndef SO_SYNCHRONOUS_NONALERT
  111. #define SO_SYNCHRONOUS_NONALERT 0x20;
  112. #endif
  113. #ifndef SO_OPENTYPE
  114. #define SO_OPENTYPE 0x7008
  115. #endif
  116. int opt = SO_SYNCHRONOUS_NONALERT;
  117. setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char*)&opt, sizeof(opt));
  118. #endif
  119. }
  120. inline Server::~Server()
  121. {
  122. #ifdef _WIN32
  123. WSACleanup();
  124. #endif
  125. }
  126. inline void Server::get(const char* pattern, Handler handler)
  127. {
  128. handlers_.insert(std::make_pair(pattern, handler));
  129. }
  130. inline void Server::post(const char* pattern, Handler handler)
  131. {
  132. handlers_.insert(std::make_pair(pattern, handler));
  133. }
  134. inline bool Server::run(const const char*ipaddr_or_hostname, int port)
  135. {
  136. sock_ = create_server_socket(ipaddr_or_hostname, port);
  137. if (sock_ == -1) {
  138. return false;
  139. }
  140. for (;;) {
  141. socket_t fd = accept(sock_, NULL, NULL);
  142. if (fd == -1) {
  143. // The server socket was closed by user.
  144. if (sock_ == -1) {
  145. return true;
  146. }
  147. close_socket(sock_);
  148. return false;
  149. }
  150. #ifdef _WIN32
  151. int osfhandle = _open_osfhandle(fd, _O_RDONLY);
  152. FILE* fp_read = fdopen(osfhandle, "r");
  153. FILE* fp_write = fdopen(osfhandle, "w");
  154. #else
  155. FILE* fp_read = fdopen(fd, "r");
  156. FILE* fp_write = fdopen(fd, "w");
  157. #endif
  158. process_request(fp_read, fp_write);
  159. fflush(fp_write);
  160. close_socket(fd);
  161. }
  162. // NOTREACHED
  163. }
  164. inline void Server::stop()
  165. {
  166. close_socket(sock_);
  167. sock_ = -1;
  168. }
  169. inline bool read_request_line(FILE* fp, std::string& method, std::string& url)
  170. {
  171. static std::regex re("(GET|POST) (.+) HTTP/1\\.1\r\n");
  172. const size_t BUFSIZ_REQUESTLINE = 2048;
  173. char buf[BUFSIZ_REQUESTLINE];
  174. fgets(buf, BUFSIZ_REQUESTLINE, fp);
  175. std::cmatch m;
  176. if (std::regex_match(buf, m, re)) {
  177. method = std::string(m[1]);
  178. url = std::string(m[2]);
  179. return true;
  180. }
  181. return false;
  182. }
  183. inline void read_headers(FILE* fp, Map& headers)
  184. {
  185. static std::regex re("(.+?): (.+?)\r\n");
  186. const size_t BUFSIZ_HEADER = 2048;
  187. char buf[BUFSIZ_HEADER];
  188. while (fgets(buf, BUFSIZ_HEADER, fp) && strcmp(buf, "\r\n")) {
  189. std::cmatch m;
  190. if (std::regex_match(buf, m, re)) {
  191. auto key = std::string(m[1]);
  192. auto val = std::string(m[2]);
  193. headers[key] = val;
  194. }
  195. }
  196. }
  197. inline void write_plain_text(FILE* fp, const char* s)
  198. {
  199. fprintf(fp, "HTTP/1.0 200 OK\r\n");
  200. fprintf(fp, "Content-type: text/plain\r\n");
  201. fprintf(fp, "Connection: close\r\n");
  202. fprintf(fp, "\r\n");
  203. fprintf(fp, "%s", s);
  204. }
  205. inline void write_error(FILE* fp, int code)
  206. {
  207. const char* msg = NULL;
  208. switch (code) {
  209. case 400:
  210. msg = "Bad Request";
  211. break;
  212. case 404:
  213. msg = "Not Found";
  214. break;
  215. default:
  216. code = 500;
  217. msg = "Internal Server Error";
  218. break;
  219. }
  220. assert(msg);
  221. fprintf(fp, "HTTP/1.0 %d %s\r\n", code, msg);
  222. fprintf(fp, "Content-type: text/plain\r\n");
  223. fprintf(fp, "Connection: close\r\n");
  224. fprintf(fp, "\r\n");
  225. fprintf(fp, "Status: %d\r\n", code);
  226. }
  227. inline void Server::process_request(FILE* fp_read, FILE* fp_write)
  228. {
  229. // Read and parse request line
  230. std::string method, url;
  231. if (!read_request_line(fp_read, method, url)) {
  232. write_error(fp_write, 400);
  233. return;
  234. }
  235. // Read headers
  236. Map headers;
  237. read_headers(fp_read, headers);
  238. // Write content
  239. char buf[BUFSIZ];
  240. std::string content;
  241. sprintf(buf, "Method: %s, URL: %s\n", method.c_str(), url.c_str());
  242. content += buf;
  243. //for (const auto& x : headers) {
  244. for (auto it = headers.begin(); it != headers.end(); ++it) {
  245. const auto& x = *it;
  246. sprintf(buf, "%s: %s\n", x.first.c_str(), x.second.c_str());
  247. content += buf;
  248. }
  249. write_plain_text(fp_write, content.c_str());
  250. }
  251. } // namespace httpsvrkit
  252. // vim: et ts=4 sw=4 cin cino={1s ff=unix