httpsvrkit.h 6.5 KB

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