// // AjaxIME HTTP Server // // Copyright(C) 2005-2007 Taku Kudo // #include #include #include #include #include #include #include #include #include #include #include #include #include "httpd.h" #include "thread.h" #ifndef SOMAXCONN #define SOMAXCONN 5 #endif namespace { static const int kTimeout = 7; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void chomp(char *s) { int len = std::strlen(s); if (len == 0) return; if (len == 1) { if (s[0] == '\n' || s[0] == '\r') s[0] = '\0'; return; } if (s[len - 1] == '\n') s[--len] = '\0'; if (s[len - 1] == '\r') s[--len] = '\0'; return; } template inline size_t tokenize(char *str, const char *del, Iterator out, size_t max) { char *stre = str + std::strlen(str); const char *dele = del + std::strlen(del); size_t size = 0; while (size < max) { char *n = std::find_first_of(str, stre, del, dele); *n = '\0'; *out++ = str; ++size; if (n == stre) break; str = n + 1; } return size; } void decodeURI(const char *src, std::string *dest) { dest->clear(); while (*src != '\0') { if (*src == '%') { char h = std::toupper(src[1]); char l = std::toupper(src[2]); int vh = std::isalpha(h) ? (10 +(h -'A')) : (h -'0'); int vl = std::isalpha(l) ? (10 +(l -'A')) : (l -'0'); *dest += ((vh << 4) + vl); src += 3; } else if (*src == '+') { *dest += ' '; src++; } else { *dest += *src++; } } } int open_server_socket(unsigned short port) { int sfd; struct sockaddr_in sin; if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { std::cerr << "socket failed" << std::endl; return -1; } memset(&sin, 0, sizeof(sin)); sin.sin_port = htons(port); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_ANY); int on = 1; setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&on), sizeof(on)); if (bind(sfd,(struct sockaddr *)&sin, sizeof(sin)) < 0) { std::cerr << "bind failed" << std::endl; close(sfd); return -1; } if (listen(sfd, SOMAXCONN) < 0) { std::cerr << "listen failed" << std::endl; close(sfd); return -1; } return sfd; } } namespace tiny_http_server { void HTTPWorker::parse_cgi_param(const char *key) { char buf[1024]; std::strncpy(buf, key, sizeof(buf)); char *params[128]; size_t n = tokenize(buf, "&", params, sizeof(params)); for (size_t i = 0; i < n; ++i) { char *pairs[2]; size_t c = tokenize(params[i], "=", pairs, sizeof(pairs)); if (c != 2) continue; std::string value; decodeURI(pairs[1], &value); cgi_param_.insert(std::make_pair (pairs[0], value)); } } bool HTTPWorker::is_read_timeout() { fd_set fds; struct timeval tv; FD_ZERO(&fds); FD_SET(client_socket_, &fds); tv.tv_sec = kTimeout; tv.tv_usec = 0; if (select(client_socket_ + 1, &fds, NULL, NULL, &tv) < 0) return true; if (FD_ISSET(client_socket_, &fds)) return false; return true; } bool HTTPWorker::is_write_timeout() { fd_set fds; struct timeval tv; FD_ZERO(&fds); FD_SET(client_socket_, &fds); tv.tv_sec = kTimeout; tv.tv_usec = 0; if (select(client_socket_ + 1, NULL, &fds, NULL, &tv) < 0) return true; if (FD_ISSET(client_socket_, &fds)) return false; return true; } bool HTTPWorker::parse_request() { std::string buffer; buffer.reserve(8192 * 10); clear(); char buf[1024]; try { while (true) { if (is_read_timeout()) return false; int size = recv(client_socket_, buf, sizeof(buf), 0); if (size == 0) break; buffer.append(buf, size); if ((buffer.size() + size) >= 8192 * 10) return false; if (buffer.size() >= 4 && std::strcmp(buffer.c_str() + buffer.size() - 4, "\r\n\r\n") == 0) break; } std::istringstream is(buffer); size_t line_num = 0; char *col[2]; while (is.getline(buf, sizeof(buf))) { chomp(buf); if (std::strlen(buf) == 0) continue; size_t size = tokenize(buf, " ", col, 2); if (line_num++ == 0) { if (size < 2) return false; if (std::strcmp(col[0], "GET") != 0) return false; if (std::strncmp(col[1], "/", 1) != 0) return false; if (std::strncmp(col[1], "/?", 2) == 0) { parse_cgi_param(col[1] + 2); } else { parse_cgi_param(col[1] + 1); } } else { if (size != 2) return false; input_header_.insert(std::make_pair (col[0], col[1])); } } } catch (...) { return false; } return true; } void HTTPWorker::clear() { input_header_.clear(); cgi_param_.clear(); output_header_.str(""); output_body_.str(""); } void HTTPWorker::send_request(const char *status) { std::string buf = "HTTP/1.1 "; output_header() << "Server: "<< server_name() << "\r\n"; output_header() << "Connection: close\r\n"; output_header() << "Content-Length: " << output_body().str().size() << "\r\n"; output_header() << "\r\n"; buf += status; buf += "\r\n"; buf += output_header().str(); buf += output_body().str(); if (is_write_timeout()) return; const char *p = buf.c_str(); int len = static_cast(buf.size()); while (len > 0) { int n = send(client_socket_, p, len, MSG_NOSIGNAL); if (n < 0) break; p += n; len -= n; } return; } const char *HTTPWorker::client_address() const { return client_address_.c_str(); } const char *HTTPWorker::server_name() const { return "GenericHTTPServer"; } void HTTPWorker::bad_request() { clear(); output_header() << "Content-type: text/plain\r\n"; output_body() << "bad request"; send_request("400 BAD REQUEST"); return; } const std::string HTTPWorker::input_header(const char *key) const { std::map::const_iterator it = input_header_.find(key); if (it == input_header_.end()) return std::string(""); return it->second; } const std::string HTTPWorker::cgi_param(const char *key) const { std::map::const_iterator it = cgi_param_.find(key); if (it == cgi_param_.end()) return std::string(""); return it->second; } void HTTPWorker::run(int client_socket, const struct sockaddr_in &client) { client_socket_ = client_socket; client_address_ = inet_ntoa(client.sin_addr); if (parse_request()) { handle_request(); // override } else { bad_request(); } close(client_socket_); shutdown(client_socket_, SHUT_RDWR); } class PreForkHTPPServerThread: public thread { public: int server_socket; HTTPWorker *worker; void run() { struct sockaddr_in client; socklen_t client_len = sizeof(client); std::cout << "Running thread: " << getpid() << std::endl; while (true) { pthread_mutex_lock(&mutex); int client_socket = accept(server_socket, (struct sockaddr *)&client, &client_len); pthread_mutex_unlock(&mutex); if (client_socket == 0) break; worker->run(client_socket, client); } delete worker; } }; int PreForkHTPPServer::run(unsigned short port, size_t prefork_num) { if (prefork_num == 0 || prefork_num >= 10) { std::cerr << "prefork_num is invalid [1..10]" << prefork_num << std::endl; return -1; } int server_socket = open_server_socket(port); if (server_socket < 0) { std::cerr << "failed to make server sokect on port: " << port << std::endl; return -1; } std::vector threads(prefork_num); for (size_t i = 0; i < prefork_num; ++i) { threads[i].worker = creator_->create(); threads[i].server_socket = server_socket; threads[i].start(); } for (size_t i = 0; i < prefork_num; ++i) { threads[i].join(); } return 0; } }