#include #include #include #include #include #include #include #include #include #include "server.h" Server::Server() { main_socket = -1; close_server = false; header_index = 0; } Server::~Server() { Close(); } void Server::CloseMainSocket() { if( main_socket != -1 ) { close(main_socket); main_socket = -1; } } void Server::CloseClientSockets() { for(size_t i=0 ; i 0 ) { if( FD_ISSET(main_socket, &read_set) ) { AddNewClient(); } else { ReadInputFromClients(); } } } } int Server::AddSocketsToSet() { FD_ZERO(&read_set); FD_ZERO(&write_set); int fd_max = 0; if( main_socket >= 0 ) { FD_SET(main_socket, &read_set); fd_max = main_socket; for(size_t i=0 ; i fd_max ) fd_max = client_tab[i].socket; } } else { std::cout << "there is no main socket" << std::endl; } return fd_max; } void Server::AddNewClient() { struct sockaddr_in client_addr; socklen_t len; int client_socket; int optval; memset(&client_addr, 0, sizeof(client_addr)); len = sizeof(client_addr); client_socket = accept(main_socket, (struct sockaddr*)&client_addr, &len); if( client_socket < 0 ) { std::cout << "accept failed" << std::endl; } else { int opt = fcntl(client_socket, F_GETFL); if( opt >= 0 ) { opt = opt | O_NONBLOCK; if( fcntl(client_socket, F_SETFL, opt) >= 0 ) { Client c; c.socket = client_socket; client_tab.push_back(c); client_tab.back().PrepareToNewRequest(); } else { std::cout << "I cannot fcntl (O_NONBLOCK)" << std::endl; } } } } // change to a better name as it is used to write to a client too void Server::ReadInputFromClients() { size_t i = 0; while( i < client_tab.size() ) { if( FD_ISSET(client_tab[i].socket, &read_set)) { ReadInputFromClient(client_tab[i]); } if( client_tab[i].answer_generated && FD_ISSET(client_tab[i].socket, &write_set) ) { WriteOutputToClient(client_tab[i]); } if( client_tab[i].output_buffer.empty() && client_tab[i].close_connection ) { std::cout << "closing connection for client nr " << client_tab[i].socket << std::endl; close(client_tab[i].socket); client_tab.erase(client_tab.begin() + i); } else { i += 1; } } } void Server::ReadInputFromClient(Client & client) { std::cout << "trying read something from client number: " << client.socket << std::endl; // we need at least one char more (for terminating zero for logging/debugging) if( client.in_buffer_max_len - client.in_buffer_len < 2 ) { client.input_buffer.append(client.in_buffer, client.in_buffer_len); client.in_buffer_len = 0; } char * old_pointer = client.in_buffer + client.in_buffer_len; int read_len = read(client.socket, client.in_buffer + client.in_buffer_len, client.in_buffer_max_len - client.in_buffer_len - 1); if( read_len < 0 ) { // read failed, we do not parse this request std::cout << "read failed" << std::endl; client.close_connection = true; //RemoveClientSocket(client.socket); // do not use client reference anymore } else if( read_len == 0 ) { // end if( client.in_buffer_len > 0 ) { client.input_buffer.append(client.in_buffer, client.in_buffer_len); client.in_buffer_len = 0; } std::cout << "------------------------------ client: " << client.socket << " calosc komunikatu " << std::endl; std::cout << client.input_buffer; std::cout << "----------------------------------------" << std::endl; std::cout << "closing connection, is it correct here?" << std::endl; client.close_connection = true; // is it correct here? should we send an answer before? //RemoveClientSocket(client.socket); // do not use client reference anymore } else { client.in_buffer_len += read_len; size_t input_buffer_index = client.input_buffer.size(); client.input_buffer.append(client.in_buffer, client.in_buffer_len); client.in_buffer_len = 0; if( client.parsing_headers ) CheckHeaders(client, input_buffer_index); client.in_buffer[client.in_buffer_len] = 0; // only for logging (we have at least one character more for terminating zero) std::cout << "------------------------------ client: " << client.socket << std::endl; std::cout << old_pointer; std::cout << "----------------------------------------" << std::endl; } } void Server::WriteOutputToClient(Client & client) { std::cout << "writing to client nr " << client.socket << std::endl; const char * data = client.output_buffer.c_str() + client.output_buffer_sent; size_t how_many_to_send = client.output_buffer.size() - client.output_buffer_sent; if( how_many_to_send > 0 ) { int len = send(client.socket, data, how_many_to_send, 0); if( len < 0 ) { std::cout << "writing failed" << std::endl; } else { client.output_buffer_sent += len; if( client.output_buffer_sent == client.output_buffer.size() ) { client.output_buffer.clear(); client.output_buffer_sent = 0; } } } } void Server::CheckHeaders(Client & client, size_t input_buffer_index) { // at least 3 bytes before if( input_buffer_index > 3 ) input_buffer_index -= 3; else input_buffer_index = 0; for( ; input_buffer_index + 3 < client.input_buffer.size() ; ++input_buffer_index) { if( IsHeadersEnding(client.input_buffer.c_str() + input_buffer_index) ) { ParseHeaders(client); client.parsing_headers = false; client.input_buffer.erase(0, input_buffer_index + 4); // temporarily we are using only get method with http 1.0 (closing connection after answering) if( client.http_method == http_method_get ) { CreateAnswer(client); client.answer_generated = true; std::cout << "answer generated, closing connection" << std::endl; client.close_connection = true; } break; } } } // ptr buffer should consists of at least more 3 characters bool Server::IsHeadersEnding(const char * ptr) { return ptr[0] == '\r' && ptr[0+1] == '\n' && ptr[0+2] == '\r' && ptr[0+3] == '\n'; } void Server::ParseHeaders(Client & client) { header_index = 0; if( !ParseFirstHeader(client) ) { std::cout << "incorrect first header, closing connection" << std::endl; client.close_connection = true; return; } // we are testing header_index + 3 < client.input_buffer.size() because we know // that the \r\n\r\n sequence already exists in this string // so there is no a problem that we leave some characters at the end of the string while( header_index + 3 < client.input_buffer.size() && !IsHeadersEnding(client.input_buffer.c_str() + header_index) ) { tmp_header.clear(); tmp_value.clear(); if( ParseHeaderKey(client) ) { SkipWhite(client); ParseHeaderValue(client); TrimWhiteAtEnd(tmp_value); std::wcout << L"wczytalem naglowek" << std::endl; std::wcout << tmp_header << L"|" << std::endl; std::wcout << tmp_value << L"|" << std::endl; if( tmp_header.size() > 0 ) client.in.Add(tmp_header, tmp_value); } else { // add some code to skip this line } } } bool Server::ParseFirstHeader(Client & client) { return ParseFirstHeaderMethodName(client) && ParseFirstHeaderURL(client) && ParseFirstHeaderHTTPVersion(client); } bool Server::ParseFirstHeaderMethodName(Client & client) { wchar_t method_name[32]; size_t method_name_index = 0; while( header_index < client.input_buffer.size() && client.input_buffer[header_index] != '\r' && !IsWhite(client.input_buffer[header_index]) ) { wchar_t c = (unsigned char)client.input_buffer[header_index++]; method_name[method_name_index++] = c; if( method_name_index >= sizeof(method_name) / sizeof(wchar_t) ) return false; } method_name[method_name_index++] = 0; SkipWhite(client); return SelectMethodName(client, method_name); } bool Server::SelectMethodName(Client & client, wchar_t * method_name) { client.http_method = http_method_unsupported; if( CompareNoCase(method_name, L"get") ) { client.http_method = http_method_get; } else if( CompareNoCase(method_name, L"put") ) { client.http_method = http_method_put; } else if( CompareNoCase(method_name, L"post") ) { client.http_method = http_method_post; } else if( CompareNoCase(method_name, L"delete") ) { client.http_method = http_method_delete; } else if( CompareNoCase(method_name, L"options") ) { client.http_method = http_method_options; } return client.http_method != http_method_unsupported; } /* * The generic URI syntax mandates that new URI schemes that provide for the representation * of character data in a URI must, in effect, represent characters from the unreserved set * without translation, and should convert all other characters to bytes according to UTF-8, * and then percent-encode those values. This requirement was introduced in January 2005 * with the publication of RFC 3986. URI schemes introduced before this date are not affected. * * */ bool Server::ParseFirstHeaderURL(Client & client) { client.url.clear(); url_ascii.clear(); while( header_index < client.input_buffer.size() && client.input_buffer[header_index] != '\r' && !IsWhite(client.input_buffer[header_index]) ) { wchar_t c = (unsigned char)client.input_buffer[header_index++]; if( c == '+' ) { c = ' '; } else if( c == '%' ) { if( header_index + 2 < client.input_buffer.size() ) { wchar_t c1 = ToLower((unsigned char)client.input_buffer[header_index++]); wchar_t c2 = ToLower((unsigned char)client.input_buffer[header_index++]); if( IsHexDigit(c1) && IsHexDigit(c2) ) { int v1 = HexDigitToValue(c1); int v2 = HexDigitToValue(c2); c = static_cast((v1 << 4) + v2); } else { return false; } } else { return false; } } else if( c < 32 || c > 127 ) { return false; } url_ascii += static_cast(c); } bool utf8_correct = PT::UTF8ToWide(url_ascii, client.url); SkipWhite(client); url_ascii.clear(); return utf8_correct && !client.url.empty(); } bool Server::ParseFirstHeaderHTTPVersion(Client & client) { client.http_version = http_version_unsupported; if( header_index + 7 < client.input_buffer.size() && client.input_buffer[header_index] == 'H' && client.input_buffer[header_index + 1] == 'T' && client.input_buffer[header_index + 2] == 'T' && client.input_buffer[header_index + 3] == 'P' && client.input_buffer[header_index + 4] == '/' && IsDecDigit(client.input_buffer[header_index + 5]) && client.input_buffer[header_index + 6] == '.' && IsDecDigit(client.input_buffer[header_index + 7]) ) { int d1 = client.input_buffer[header_index + 5] - '0'; int d2 = client.input_buffer[header_index + 7] - '0'; int ddd1 = client.input_buffer[header_index + 5] - '0'; int ddd2 = client.input_buffer[header_index + 7] - '0'; if( d1 == 1 ) { client.http_version = http_version_1_0; } if( d2 == 1 ) { client.http_version = http_version_1_1; } if( ddd1 == 1 ) { client.http_version = http_version_1_0; } if( ddd2 == 1 ) { client.http_version = http_version_1_1; } if( d1 == 1 ) { if( d2 == 0 ) client.http_version = http_version_1_0; else if( d2 == 1 ) client.http_version = http_version_1_1; } header_index += 8; SkipWhite(client); } return client.http_version != http_version_unsupported; } // ParseHeaderKey should increment header_index at least once bool Server::ParseHeaderKey(Client & client) { while( header_index < client.input_buffer.size() && client.input_buffer[header_index] != '\r' ) { if( client.input_buffer[header_index] == ':' ) { header_index += 1; return true; } wchar_t c = (unsigned char)client.input_buffer[header_index]; if( c >= 32 && c < 127 ) { // allow only asci characters tmp_header += c; } header_index += 1; } // there was not a colon at the end of the name header_index += 1; return false; } void Server::ParseHeaderValue(Client & client) { while( header_index < client.input_buffer.size() ) { if( header_index + 1 < client.input_buffer.size() && client.input_buffer[header_index] == '\r' && client.input_buffer[header_index+1] == '\n' ) { if( header_index + 2 < client.input_buffer.size() && (client.input_buffer[header_index+2] == ' ' || client.input_buffer[header_index+2] == '\t') ) { // this line will be continued in the next line header_index += 3; } else { header_index += 2; break; } } else { wchar_t c = (unsigned char)client.input_buffer[header_index]; if( c >= 32 && c < 127 ) { // allow only ascii characters tmp_value += c; } header_index += 1; } } } //void Server::RemoveClientSocket(int client_socket) //{ // for(size_t i=0 ; i= '0' && c <= '9' ) return true; return false; } bool Server::IsHexDigit(wchar_t c) { if( c >= '0' && c <= '9' ) return true; if( ToLower(c) >= 'a' && ToLower(c) <= 'f' ) return true; return false; } int Server::HexDigitToValue(wchar_t c) { if( c >= '0' && c <= '9' ) return static_cast(c - '0'); if( ToLower(c) >= 'a' && ToLower(c) <= 'f' ) return static_cast(c - 'a') + 10; return 0; } wchar_t Server::ToLower(wchar_t c) { if( c >= 'A' && c <= 'Z' ) c = c - 'A' + 'a'; return c; } bool Server::CompareNoCase(const wchar_t * str1, const wchar_t * str2) { while( *str1 && *str2 && ToLower(*str1) == ToLower(*str2) ) { ++str1; ++str2; } if( *str1 == 0 && *str2 == 0 ) return true; return false; } bool Server::CompareNoCase(const std::wstring & str1, const wchar_t * str2) { return CompareNoCase(str1.c_str(), str2); } // do not use \r or \n here bool Server::IsWhite(wchar_t c) { return (c == ' ' || c == '\t'); } void Server::TrimWhiteAtEnd(std::wstring & str) { if( !str.empty() ) { size_t i = str.size(); while( i > 0 && IsWhite(str[i-1]) ) { i -= 1; } if( i < str.size() ) str.erase(i); } } void Server::SkipWhite(Client & client) { while( header_index < client.input_buffer.size() && IsWhite(client.input_buffer[header_index]) ) { header_index += 1; } } void Server::CreateAnswer(Client & client) { std::wstring a; a = L"HTTP/1.0 200 OK\r\n"; a += L"Content-Type: text/html; charset=UTF-8\r\n"; a += L"\r\n"; a += L"hello world from my webserwer, your requested: " + client.url; if( client.url == L"/quit" ) { a += L"

bye bye"; close_server = true; } PT::WideToUTF8(a, client.output_buffer); }