httpsvrkit.h 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. //
  2. // httpsvrkit.h
  3. //
  4. // Copyright (c) 2012 Yuji Hirose. All rights reserved.
  5. // The Boost Software License 1.0
  6. //
  7. #include <functional>
  8. #include <map>
  9. #include <regex>
  10. #include <string>
  11. #include <assert.h>
  12. #ifdef _WIN32
  13. #include <winsock2.h>
  14. #include <ws2tcpip.h>
  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. namespace httpsvrkit
  32. {
  33. typedef std::map<std::string, std::string> Map;
  34. typedef std::multimap<std::string, std::string> MultiMap;
  35. // HTTP request
  36. struct Request {
  37. Map headers;
  38. std::string body;
  39. std::string pattern;
  40. Map params;
  41. };
  42. // HTTP response
  43. struct Response {
  44. MultiMap headers;
  45. std::string body;
  46. };
  47. struct Context {
  48. const Request request;
  49. Response response;
  50. };
  51. // HTTP server
  52. class Server {
  53. public:
  54. typedef std::function<void (Context& context)> Handler;
  55. Server();
  56. ~Server();
  57. void get(const std::string& pattern, Handler handler);
  58. void post(const std::string& pattern, Handler handler);
  59. bool run(const std::string& ipaddr, int port);
  60. void stop();
  61. private:
  62. void process_request(int fd);
  63. socket_t sock_;
  64. std::multimap<std::string, Handler> handlers_;
  65. };
  66. // Implementation
  67. template <typename Fn>
  68. void fdopen_b(int fd, const char* md, Fn fn)
  69. {
  70. #ifdef _WIN32
  71. int osfhandle = _open_osfhandle(fd, _O_RDONLY);
  72. FILE* fp = fdopen(osfhandle, md);
  73. #else
  74. FILE* fp = fdopen(fd, md);
  75. #endif
  76. if (fp) {
  77. fn(fp);
  78. fclose(fp);
  79. #ifdef _WIN32
  80. close(osfhandle);
  81. #endif
  82. }
  83. }
  84. inline socket_t create_socket(const std::string& ipaddr, int port)
  85. {
  86. // Create a server socket
  87. socket_t sock = socket(AF_INET, SOCK_STREAM, 0);
  88. if (sock == -1) {
  89. return -1;
  90. }
  91. // Make 'reuse address' option available
  92. int yes = 1;
  93. setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
  94. // Bind the socket to the given address
  95. struct sockaddr_in addr;
  96. addr.sin_family = AF_INET;
  97. addr.sin_port = htons((uint16_t)port);
  98. if (inet_aton(ipaddr.c_str(), &addr.sin_addr) <= 0) {
  99. return -1;
  100. }
  101. if (::bind(sock, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
  102. return -1;
  103. }
  104. // Listen through 5 channels
  105. if (listen(sock, 5) != 0) {
  106. return -1;
  107. }
  108. return sock;
  109. }
  110. inline void close_socket(socket_t sock)
  111. {
  112. #ifdef _WIN32
  113. closesocket(sock);
  114. #else
  115. shutdown(sock, SHUT_RDWR);
  116. close(sock);
  117. #endif
  118. }
  119. inline Server::Server()
  120. : sock_(-1)
  121. {
  122. #ifdef _WIN32
  123. WSADATA wsaData;
  124. WSAStartup(0x0002, &wsaData);
  125. #endif
  126. }
  127. inline Server::~Server()
  128. {
  129. #ifdef _WIN32
  130. WSACleanup();
  131. #endif
  132. }
  133. inline void Server::get(const std::string& pattern, Handler handler)
  134. {
  135. handlers_.insert(std::make_pair(pattern, handler));
  136. }
  137. inline void Server::post(const std::string& pattern, Handler handler)
  138. {
  139. handlers_.insert(std::make_pair(pattern, handler));
  140. }
  141. inline bool Server::run(const std::string& ipaddr, int port)
  142. {
  143. sock_ = create_socket(ipaddr, port);
  144. if (sock_ == -1) {
  145. return false;
  146. }
  147. for (;;) {
  148. socket_t fd = accept(sock_, NULL, NULL);
  149. if (fd == -1) {
  150. // The server socket was closed by user.
  151. if (sock_ == -1) {
  152. return true;
  153. }
  154. close_socket(sock_);
  155. return false;
  156. }
  157. process_request(fd);
  158. close(fd);
  159. }
  160. // NOTREACHED
  161. }
  162. inline void Server::stop()
  163. {
  164. close_socket(sock_);
  165. sock_ = -1;
  166. }
  167. inline bool read_request_line(FILE* fp, std::string& method, std::string& url)
  168. {
  169. static std::regex re("(GET|POST) (.+) HTTP/1\\.1\r\n");
  170. const size_t BUFSIZ_REQUESTLINE = 2048;
  171. char buf[BUFSIZ_REQUESTLINE];
  172. fgets(buf, BUFSIZ_REQUESTLINE, fp);
  173. std::cmatch m;
  174. if (std::regex_match(buf, m, re)) {
  175. method = std::string(m[1]);
  176. url = std::string(m[2]);
  177. return true;
  178. }
  179. return false;
  180. }
  181. inline void read_headers(FILE* fp, Map& headers)
  182. {
  183. static std::regex re("(.+?): (.+?)\r\n");
  184. const size_t BUFSIZ_HEADER = 2048;
  185. char buf[BUFSIZ_HEADER];
  186. while (fgets(buf, BUFSIZ_HEADER, fp) && strcmp(buf, "\r\n")) {
  187. std::cmatch m;
  188. if (std::regex_match(buf, m, re)) {
  189. auto key = std::string(m[1]);
  190. auto val = std::string(m[2]);
  191. headers[key] = val;
  192. }
  193. }
  194. }
  195. inline void write_plain_text(int fd, const char* s)
  196. {
  197. fdopen_b(fd, "w", [=](FILE* fp) {
  198. fprintf(fp, "HTTP/1.0 200 OK\r\n");
  199. fprintf(fp, "Content-type: text/plain\r\n");
  200. fprintf(fp, "Connection: close\r\n");
  201. fprintf(fp, "\r\n");
  202. fprintf(fp, "%s", s);
  203. });
  204. }
  205. inline void write_error(int fd, 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. fdopen_b(fd, "w", [=](FILE* fp) {
  222. fprintf(fp, "HTTP/1.0 %d %s\r\n", code, msg);
  223. fprintf(fp, "Content-type: text/plain\r\n");
  224. fprintf(fp, "Connection: close\r\n");
  225. fprintf(fp, "\r\n");
  226. fprintf(fp, "Status: %d\r\n", code);
  227. });
  228. }
  229. inline void Server::process_request(int fd)
  230. {
  231. fdopen_b(fd, "r", [=](FILE* fp) {
  232. // Read and parse request line
  233. std::string method, url;
  234. if (!read_request_line(fp, method, url)) {
  235. write_error(fd, 400);
  236. return;
  237. }
  238. // Read headers
  239. Map headers;
  240. read_headers(fp, 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. sprintf(buf, "%s: %s\n", x.first.c_str(), x.second.c_str());
  248. content += buf;
  249. }
  250. write_plain_text(fd, content.c_str());
  251. });
  252. }
  253. } // namespace httpsvrkit
  254. // vim: et ts=4 sw=4 cin cino={1s ff=unix