/* * socket.cpp * * Created on: May 16, 2019 * Author: cmy */ #include "server.h" using std::string; using std::vector; using std::cout; using std::endl; using std::runtime_error; namespace chttp { Server::Server() { // TODO Auto-generated constructor stub } Server::~Server() { // TODO Auto-generated destructor stub } std::string Server::ReadString(Sock_type sock, size_t count) { char c{}; string s{}; for (size_t i = 0; i < count; ++i) { recv(sock, &c, 1, 0); s.push_back(c); } return s; } int Server::GetLine(Sock_type sock, char* buf, int size) { //#ifdef __linux__ int count { }; char c = '\0'; int n { }; while ((count < size - 1) && (c != '\n')) { n = recv(sock, &c, 1, 0); if (n > 0) { if (c == '\r') { n = recv(sock, &c, 1, MSG_PEEK); //peek the next char if ((n > 0) && (c == '\n')) { recv(sock, &c, 1, 0); break; } else c = '\n'; } buf[count] = c; count++; } else { c = '\n'; } } buf[count] = '\0'; return count; //#endif } vector Server::ReadRequest(const Sock_type& clnt_sock) { int char_count { 1 }; char buffer[buffer_size]; vector content { }; while (char_count > 0 && strcmp("\n", buffer)) { char_count = GetLine(clnt_sock, buffer, sizeof(buffer)); content.push_back(buffer); } return content; } Server::Sock_type Server::StartUpSocket(const Port_type& port) { #ifdef __linux__ int serv_sock { }; sockaddr_in serv_addr { }; serv_sock = socket(PF_INET, SOCK_STREAM, 0); if (serv_sock == -1) { throw std::runtime_error { "socket_create_error" }; } serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(port); serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(serv_sock, (sockaddr*) (&serv_addr), sizeof(serv_addr)) < 0) { throw std::runtime_error { "socket_bind_error" }; } if (listen(serv_sock, 5) == -1) { throw std::runtime_error { "socket_listen_error" }; } return serv_sock; #endif __linux__ //__linux__ #ifdef _MSC_VER WSADATA wsaData{}; SOCKET serv_sock{}; SOCKADDR_IN serv_addr; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { throw std::runtime_error{ "win_socket_startup_error" }; } serv_sock = socket(PF_INET, SOCK_STREAM, 0); if (serv_sock == INVALID_SOCKET) { throw std::runtime_error{ "socket_create_error" }; } serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(port); if (bind(serv_sock, (SOCKADDR*)& serv_addr, sizeof(serv_addr)) == SOCKET_ERROR) { throw std::runtime_error{ "socket_bind_error" }; } if (listen(serv_sock, 5) == SOCKET_ERROR) { throw std::runtime_error{ "socket_listen_error" }; } return serv_sock; #endif // _MSC_VER } void Server::ProcessRequest(Sock_type clnt_sock) { auto request_string = ReadRequest(clnt_sock); auto request_line = ParseRequestLine(request_string); Request_Method method = std::get<0>(request_line); URL_type url = URL{ std::get<1>(request_line) }; std::string url_copy = url.GetURL(); auto count = url_copy.find_first_of('?'); std::string valid_path = url.GetURL().substr(0, count); Http_Version version = std::get<2>(request_line); Headers header { }; for (auto it = request_string.begin() + 1; it + 1 != request_string.end(); // ignore the last empty line ++it) { auto pair = GetKeyValuePair(*it); header.SetHeader(pair.first, pair.second); } // get request body int content_length = header.ContentLength(); std::string request_body = ReadString(clnt_sock, static_cast(content_length)); cout << "url: " << valid_path << endl; cout << "Headers:\n" << header << endl; // TODO add client info to request Request request{ method, url, version, header, request_body }; Response response { clnt_sock }; RequestInfo request_info { valid_path, method }; if (http_router.Contains(request_info)) { auto handler = http_router.GetHandler(request_info); handler(request, response); cout << "find existing routers" << endl; } else if (http_router.Contains({ valid_path, Request_Method::ALL })) { auto handler = http_router.GetHandler(request_info); handler(request, response); cout << "find existing routers" << endl; } else { cout << "not find existing routers" << endl; response.NotFound(); } } std::string::const_iterator Server::NextWord(std::string::const_iterator begin, std::string::const_iterator end) { for (auto it = begin; it != end; ++it) { if (*it == ' ' || *it == '\0') { return it; } } return end; } std::tuple Server::ParseRequestLine( const std::vector& request_data) { auto method_string_end = NextWord(request_data[0].cbegin(), request_data[0].cend()); auto url_string_end = NextWord(method_string_end + 1, request_data[0].end()); auto method_string = string { request_data[0].cbegin(), method_string_end }; auto url_string = string { method_string_end + 1, url_string_end }; auto version_string_end = NextWord(url_string_end + 1, request_data[0].end()); auto version_string = string { url_string_end + 1, version_string_end }; auto method = GetRequestMethod(method_string); auto url = GetRequestUrl(url_string); auto http_version = GetHttpVersion(version_string.c_str()); return std::make_tuple(method, url, http_version); } Request_Method Server::GetRequestMethod(const std::string& str) { auto c_str = str.c_str(); if (StrCompare(c_str, "get") == 0) { return Request_Method::GET; } else if (StrCompare(c_str, "post") == 0) { return Request_Method::POST; } else if (StrCompare(c_str, "head") == 0) { return Request_Method::HEAD; } else if (StrCompare(c_str, "put") == 0) { return Request_Method::PUT; } else if (StrCompare(c_str, "delete") == 0) { return Request_Method::DELETE_M; } else if (StrCompare(c_str, "options") == 0) { return Request_Method::OPTIONS; } else if (StrCompare(c_str, "connect") == 0) { return Request_Method::CONNECT; } else if (StrCompare(c_str, "trace") == 0) { return Request_Method::TRACE; } else { return Request_Method::NONE; } } Http_Version Server::GetHttpVersion(const std::string& str) { auto c_str = str.c_str(); if (StrCompare(c_str, "HTTP/1.1")) { return Http_Version::HTTP1_1; } else if (StrCompare(c_str, "HTTP/1.0")) { return Http_Version::HTTP1_0; } else if (StrCompare(c_str, "HTTP/2.0")) { return Http_Version::HTTP2_0; } else { return Http_Version::NONE; } } URL_type Server::GetRequestUrl(const std::string& str) { return URL_type { str }; } std::pair Server::GetKeyValuePair( const std::string& str) { auto it = str.find(": "); if (it == string::npos) { throw runtime_error { "no colon-blank separator found" }; } string key { str.cbegin(), str.begin() + it }; string value { str.begin() + it + 2, str.end() }; return make_pair(key, value); } inline int Server::StrCompare(const char* str1, const char* str2) { #ifdef __linux__ return strcasecmp(str1, str2); #endif #ifdef _MSC_VER return _stricmp(str1, str2); #endif } void Server::CloseSocket(Sock_type sock) { #ifdef __linux__ return close(sock); #elif _MSC_VER closesocket(sock); return; #endif } } /* namespace chttp */