httpsvrkit.h 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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. // HTTP request
  34. class Request {
  35. public:
  36. std::map<std::string, std::string> headers_;
  37. std::string body_;
  38. std::string pattern_;
  39. std::map<std::string, std::string> params_;
  40. };
  41. // HTTP response
  42. class Response {
  43. public:
  44. std::multimap<std::string, std::string> headers_;
  45. std::string body_;
  46. };
  47. // HTTP server
  48. class Server {
  49. public:
  50. typedef std::function<void (const Request&, Response& res)> Handler;
  51. Server();
  52. ~Server();
  53. void get(const std::string& pattern, Handler handler);
  54. void post(const std::string& pattern, Handler handler);
  55. bool run(const std::string& ipaddr, int port);
  56. void stop();
  57. private:
  58. void process_request(int fd);
  59. const size_t BUFSIZ_REQUESTLINE = 2048;
  60. socket_t sock_;
  61. std::multimap<std::string, Handler> handlers_;
  62. };
  63. // Implementation
  64. template <typename Fn>
  65. void fdopen_b(int fd, const char* md, Fn fn)
  66. {
  67. #ifdef _WIN32
  68. int osfhandle = _open_osfhandle(fd, _O_RDONLY);
  69. FILE* fp = fdopen(osfhandle, md);
  70. #else
  71. FILE* fp = fdopen(fd, md);
  72. #endif
  73. if (fp) {
  74. fn(fp);
  75. fclose(fp);
  76. #ifdef _WIN32
  77. close(osfhandle);
  78. #endif
  79. }
  80. }
  81. inline socket_t create_socket(const std::string& ipaddr, int port)
  82. {
  83. // Create a server socket
  84. socket_t sock = socket(AF_INET, SOCK_STREAM, 0);
  85. if (sock == -1) {
  86. return -1;
  87. }
  88. // Make 'reuse address' option available
  89. int yes = 1;
  90. setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
  91. // Bind the socket to the given address
  92. struct sockaddr_in addr;
  93. addr.sin_family = AF_INET;
  94. addr.sin_port = htons((uint16_t)port);
  95. if (inet_aton(ipaddr.c_str(), &addr.sin_addr) <= 0) {
  96. return -1;
  97. }
  98. if (::bind(sock, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
  99. return -1;
  100. }
  101. // Listen through 5 channels
  102. if (listen(sock, 5) != 0) {
  103. return -1;
  104. }
  105. return sock;
  106. }
  107. inline void close_socket(socket_t sock)
  108. {
  109. #ifdef _WIN32
  110. closesocket(sock);
  111. #else
  112. shutdown(sock, SHUT_RDWR);
  113. close(sock);
  114. #endif
  115. }
  116. inline Server::Server()
  117. : sock_(-1)
  118. {
  119. #ifdef _WIN32
  120. WSADATA wsaData;
  121. WSAStartup(0x0002, &wsaData);
  122. #endif
  123. }
  124. inline Server::~Server()
  125. {
  126. #ifdef _WIN32
  127. WSACleanup();
  128. #endif
  129. }
  130. inline void Server::get(const std::string& pattern, Handler handler)
  131. {
  132. handlers_.insert(std::make_pair(pattern, handler));
  133. }
  134. inline void Server::post(const std::string& pattern, Handler handler)
  135. {
  136. handlers_.insert(std::make_pair(pattern, handler));
  137. }
  138. inline bool Server::run(const std::string& ipaddr, int port)
  139. {
  140. sock_ = create_socket(ipaddr, port);
  141. if (sock_ == -1) {
  142. return false;
  143. }
  144. for (;;) {
  145. socket_t fd = accept(sock_, NULL, NULL);
  146. if (fd == -1) {
  147. // The server socket was closed by user.
  148. if (sock_ == -1) {
  149. return true;
  150. }
  151. close_socket(sock_);
  152. return false;
  153. }
  154. process_request(fd);
  155. close(fd);
  156. }
  157. // NOTREACHED
  158. }
  159. inline void Server::stop()
  160. {
  161. close_socket(sock_);
  162. sock_ = -1;
  163. }
  164. inline bool parse_request_line(const char* s, std::string& cmd, std::string& url)
  165. {
  166. std::regex re("(GET|POST) (.+) HTTP/1\\.1\r\n");
  167. std::cmatch m;
  168. if (std::regex_match(s, m, re)) {
  169. cmd = std::string(m[1]);
  170. url = std::string(m[2]);
  171. return true;
  172. }
  173. return false;
  174. }
  175. inline void write_plain_text(int fd, const char* s)
  176. {
  177. fdopen_b(fd, "w", [=](FILE* fp) {
  178. fprintf(fp, "HTTP/1.0 200 OK\r\n");
  179. fprintf(fp, "Content-type: text/plain\r\n");
  180. fprintf(fp, "Connection: close\r\n");
  181. fprintf(fp, "\r\n");
  182. fprintf(fp, "%s", s);
  183. });
  184. }
  185. inline void write_error(int fd, int code)
  186. {
  187. const char* msg = NULL;
  188. switch (code) {
  189. case 400:
  190. msg = "Bad Request";
  191. break;
  192. case 404:
  193. msg = "Not Found";
  194. break;
  195. default:
  196. code = 500;
  197. msg = "Internal Server Error";
  198. break;
  199. }
  200. assert(msg);
  201. fdopen_b(fd, "w", [=](FILE* fp) {
  202. fprintf(fp, "HTTP/1.0 %d %s\r\n", code, msg);
  203. fprintf(fp, "Content-type: text/plain\r\n");
  204. fprintf(fp, "Connection: close\r\n");
  205. fprintf(fp, "\r\n");
  206. fprintf(fp, "Status: %d\r\n", code);
  207. });
  208. }
  209. inline void Server::process_request(int fd)
  210. {
  211. fdopen_b(fd, "r", [=](FILE* fp) {
  212. // Parse request line
  213. char request_line[BUFSIZ_REQUESTLINE];
  214. fgets(request_line, BUFSIZ_REQUESTLINE, fp);
  215. std::string cmd, url;
  216. if (!parse_request_line(request_line, cmd, url)) {
  217. write_error(fd, 400);
  218. return;
  219. }
  220. // Write content
  221. char content[BUFSIZ];
  222. sprintf(content, "cmd: %s, url: %s\n", cmd.c_str(), url.c_str());
  223. write_plain_text(fd, content);
  224. });
  225. }
  226. } // namespace httpsvrkit
  227. // vim: et ts=4 sw=4 cin cino={1s ff=unix