#include #include #include #include #include #include #include #include #include #include #include #include "server.h" #include "string_functions.h" Server::Server() { main_socket = -1; close_server = false; in_buffer_max_len = 2048; in_buffer = nullptr; } Server::~Server() { Close(); } void Server::CloseMainSocket() { if( main_socket != -1 ) { close(main_socket); main_socket = -1; } } void Server::CloseClientSockets() { for(auto i=client_tab.begin() ; i != client_tab.end() ; ++i) { close(i->socket); if( i->external_resource.external_resource_fd >= 0 ) close(i->external_resource.external_resource_fd); } client_tab.clear(); } void Server::Close() { CloseMainSocket(); CloseClientSockets(); } void Server::AllocInputBuffer() { delete [] in_buffer; in_buffer = new char[in_buffer_max_len]; } void Server::Prepare() { PrepareMainSocket(); AllocInputBuffer(); } void Server::PrepareMainSocket() { CloseMainSocket(); main_socket = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); if( main_socket < 0 ) { std::cout << "I cannot create a socket" << std::endl; return; } int optval = 1; if( setsockopt(main_socket, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) < 0 ) { std::cout << "I cannot setsockopt " << std::endl; return; } struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_len = sizeof(addr); addr.sin_family = PF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(3012); socklen_t len = sizeof(addr); if( bind(main_socket, (struct sockaddr*)&addr, len) < 0 ) { std::cout << "bind failed: " << std::endl; return; } if( listen(main_socket, 100) < 0 ) { std::cout << "listen failed" << std::endl; return; } } void Server::Wait() { while( !close_server ) { timeval t; int fd_max; int how_many_external_resources; PrepareSocketsForSelect(fd_max, t, how_many_external_resources); timeval * pt = 0; if( how_many_external_resources > 0 ) pt = &t; int ready_fd = select(fd_max + 1, &read_set, &write_set, 0, pt); if( how_many_external_resources > 0 ) ReadExternalResource(how_many_external_resources); if( ready_fd > 0 ) { if( FD_ISSET(main_socket, &read_set) ) { AddNewClient(); } else { ReadWriteToClients(); } } } } void Server::PrepareSocketsForSelect(int & fd_max, timeval & t, int & how_many_external_resources) { FD_ZERO(&read_set); FD_ZERO(&write_set); fd_max = 0; t.tv_sec = 0; t.tv_usec = 0; how_many_external_resources = 0; if( main_socket >= 0 ) { FD_SET(main_socket, &read_set); fd_max = main_socket; for(auto i=client_tab.begin() ; i != client_tab.end() ; ++i) { if( !i->answering_to_client ) { /* * do not read the next request until the answer is sent * (client can use pipelining) * */ FD_SET(i->socket, &read_set); } else { FD_SET(i->socket, &write_set); } if( i->socket > fd_max ) fd_max = i->socket; if( i->external_resource.reading_external_resource ) { how_many_external_resources += 1; } } } else { std::cout << "there is no main socket" << std::endl; } if( how_many_external_resources > 0 ) t.tv_usec = 1; } 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; c.close_connection = false; c.PrepareToNewRequest(); client_tab.push_back(c); } else { std::cout << "I cannot fcntl (O_NONBLOCK)" << std::endl; } } } } void Server::ReadWriteToClients() { for(auto i=client_tab.begin() ; i != client_tab.end() ; ) { if( FD_ISSET(i->socket, &read_set)) { ReadInputFromClient(*i); } if( FD_ISSET(i->socket, &write_set) ) { WriteOutputToClient(*i); } if( !i->answering_to_client && i->close_connection && (!i->external_resource.reading_external_resource || !i->external_resource.waiting_for_last_read) ) { std::cout << "closing connection for client nr " << i->socket << std::endl; close(i->socket); if( i->external_resource.external_resource_fd >= 0 ) close(i->external_resource.external_resource_fd); auto next_i = i; ++next_i; client_tab.erase(i); i = next_i; } else { ++i; } } } void Server::ReadExternalResource(int how_many_external_resources) { int count = 0; for(auto i=client_tab.begin() ; i != client_tab.end() && count < how_many_external_resources ; ++i) { if( i->external_resource.reading_external_resource ) { if( i->reading_to_buffer == 1 ) { ReadExternalResource(*i, i->output_buffer); } else if( i->reading_to_buffer == 2 ) { ReadExternalResource(*i, i->output_buffer_two); } count += 1; } } } void Server::ReadExternalResource(Client & client, std::string & output_buffer) { if( !client.external_resource.waiting_for_last_read ) { client.iocb.aio_fildes = client.external_resource.external_resource_fd; client.iocb.aio_offset = client.external_resource.file_how_many_read; client.iocb.aio_buf = (volatile void*)client.read_static_buffer; client.iocb.aio_nbytes = client.read_static_buffer_size; /* * at the beginning we have some headers in the first output buffer * * zmniejszamy ilosc bajtow do odczytania tak aby cala dlugosc bufora byla podzielna przez ilosc pakietow do wyslania * */ if( output_buffer.size() < client.iocb.aio_nbytes ) client.iocb.aio_nbytes -= output_buffer.size(); int res = aio_read(&client.iocb); if( res < 0 ) { int err = errno; std::cout << "aio_read has problems: errno: " << err << std::endl; } else { client.external_resource.waiting_for_last_read = true; } } else { int res = aio_error(&client.iocb); std::cout << "aio_error returned: " << res << std::endl; if( res == EINPROGRESS ) { std::cout << "res is equal EINPROGRESS" << std::endl; } else if( res == 0 ) { std::cout << "reading completed" << std::endl; res = aio_return(&client.iocb); if( res < 0 ) { client.reading_to_buffer = 0; client.external_resource.reading_external_resource = false; close(client.external_resource.external_resource_fd); client.external_resource.external_resource_fd = -1; int err = errno; std::cout << "aio_return returned error, errno: " << err << std::endl; } else if( res == 0 ) { std::cout << "koniec czytania pliku" << std::endl; if( client.sending_from_buffer == 0 ) client.sending_from_buffer = client.reading_to_buffer; client.reading_to_buffer = 0; client.external_resource.reading_external_resource = false; close(client.external_resource.external_resource_fd); client.external_resource.external_resource_fd = -1; } else { std::cout << "przeczytano bajtow: " << res << std::endl; output_buffer.append(client.read_static_buffer, res); client.external_resource.file_how_many_read += (size_t)res; } client.external_resource.waiting_for_last_read = false; if( output_buffer.size() > 1500 ) // send at least one full packed, add to config { if( client.reading_to_buffer == 1 ) { client.reading_to_buffer = 0; if( client.sending_from_buffer != 2 ) { client.reading_to_buffer = 2; } if( client.sending_from_buffer == 0 ) { client.sending_from_buffer = 1; } } else if( client.reading_to_buffer == 2 ) { client.reading_to_buffer = 0; if( client.sending_from_buffer != 1 ) { client.reading_to_buffer = 1; } if( client.sending_from_buffer == 0 ) { client.sending_from_buffer = 2; } } } } else { std::cout << "some error" << std::endl; // what about this buffer? } } } void Server::ReadInputFromClient(Client & client) { int read_len = read(client.socket, in_buffer, in_buffer_max_len); if( read_len < 0 ) { // read failed, we do not parse this request std::cout << "read failed for client " << client.socket << std::endl; client.close_connection = true; } else if( read_len == 0 ) { // the client has terminated connection std::cout << "read returned 0, client " << client.socket << " closed the connection" << std::endl; client.close_connection = true; } else { size_t input_buffer_index = client.input_buffer.size(); client.input_buffer.append(in_buffer, read_len); std::cout << "read from client " << client.socket << ":"; for(size_t i=0 ; i<(size_t)read_len ; ++i) std::cout << in_buffer[i]; std::cout << std::endl; if( client.parsing_headers ) CheckHeaders(client, input_buffer_index); } } void Server::WriteOutputToClient(Client & client, std::string & output_buffer, size_t & output_buffer_sent) { const char * data = output_buffer.c_str() + output_buffer_sent; size_t how_many_to_send = output_buffer.size() - output_buffer_sent; int len = 0; if( how_many_to_send > 0 ) { len = send(client.socket, data, how_many_to_send, 0); if( len < 0 ) { std::cout << "writing failed" << std::endl; } } if( len >= 0 ) { output_buffer_sent += len; if( output_buffer_sent >= output_buffer.size() ) { output_buffer.clear(); output_buffer_sent = 0; if( client.sending_from_buffer == 1 ) { client.sending_from_buffer = 0; if( client.reading_to_buffer != 2 && !client.output_buffer_two.empty() ) { client.sending_from_buffer = 2; } if( client.external_resource.reading_external_resource && client.reading_to_buffer == 0 ) { client.reading_to_buffer = 1; } } else if( client.sending_from_buffer == 2 ) { client.sending_from_buffer = 0; if( client.reading_to_buffer != 1 && !client.output_buffer.empty() ) { client.sending_from_buffer = 1; } if( client.external_resource.reading_external_resource && client.reading_to_buffer == 0 ) { client.reading_to_buffer = 2; } } } } } void Server::WriteOutputToClient(Client & client) { if( client.sending_from_buffer == 1 ) { WriteOutputToClient(client, client.output_buffer, client.output_buffer_sent); } else if( client.sending_from_buffer == 2 ) { WriteOutputToClient(client, client.output_buffer_two, client.output_buffer_two_sent); } else if( !client.external_resource.reading_external_resource && client.output_buffer.empty() && client.output_buffer_two.empty() ) { client.PrepareToNewRequest(); } } void Server::CheckHeaders(Client & client, size_t input_buffer_index) { // we check at least 3 bytes before because we are looking for \r\n\r\n // and the input_buffer_index can point at the last \n now 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( headers_parser.IsHeadersEnding(client.input_buffer.c_str() + input_buffer_index) ) { ParseHeaders(client); client.input_buffer.erase(0, input_buffer_index + 4); break; } } } void Server::ParseHeaders(Client & client) { headers_parser.ParseHeaders(client); if( !SelectMethodName(client, client.http_method_str) ) { std::cout << "unsupported http method" << std::endl; client.close_connection = true; } client.parsing_headers = false; // temporarily we are using only get method with http 1.0 (closing connection after answering) if( client.http_method == http_method_get ) { CreateAnswer(client); /* * * we cannot close the connection here (a file can be send) * if( client.http_version == http_version_1_0 ) { if( !CompareNoCase(client.in.Text(L"connection"), L"keep-alive") ) { //std::cout << "answer generated, closing connection for http 1.0" << std::endl; client.close_connection = true; } else { //std::cout << "keep-alive is set, not closing connection for http 1.0" << std::endl; } } */ } if( client.http_method != http_method_get ) { // at the moment only get method client.close_connection = true; } } bool Server::SelectMethodName(Client & client, const std::wstring & 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; } //void Server::RemoveClientSocket(int client_socket) //{ // for(size_t i=0 ; ifirst; a += L": "; a += i->second; a += L"\r\n"; PT::WideToUTF8(a, client.output_buffer, false); if( CompareNoCase(i->first, L"connection") ) has_connection = true; } if( !has_connection && CompareNoCase(client.in.Text(L"connection"), L"keep-alive") ) { client.output_buffer += "Connection: keep-alive\r\n"; } a = L"\r\n"; PT::WideToUTF8(a, client.output_buffer, false); } void Server::CreateAnswer(Client & client) { // tmp, a user method will be called std::wstring a, c; std::string c_ascii; if( client.url == L"/static/styles.css" ) { a.clear(); } if( client.url == L"/quit" ) { close_server = true; } //if( client.url == L"/swinka.jpg" ) { std::string file_name_ascii; client.send_file = L"/home/tomek/roboczy/prog/libscorpiohttpserver/www-data"; client.send_file += client.url; PT::WideToUTF8(client.send_file, file_name_ascii); if( FileLen(file_name_ascii, client.external_resource.file_len) ) { client.status = 200; //client.out_headers.Add(L"Content-Type", L"image/jpeg;"); client.out_headers.Add(L"Content-Length", client.external_resource.file_len); CreateOutputHeaders(client); if( client.external_resource.external_resource_fd != -1 ) close(client.external_resource.external_resource_fd); // improve me (utf8) client.external_resource.external_resource_fd = open(file_name_ascii.c_str(), O_RDONLY | O_NONBLOCK); if( client.external_resource.external_resource_fd >= 0 ) { std::cout << "przygotowuje do czytania pliku " << file_name_ascii << std::endl; client.reading_to_buffer = 1; client.sending_from_buffer = 0; client.answering_to_client = true; client.external_resource.reading_external_resource = true; client.external_resource.type = ExternalResource::type_file; } return; } else { client.status = 404; client.out_headers.Add(L"Content-Type", L"text/html; charset=UTF-8"); c = L"404 nie ma czegos takiego"; PT::WideToUTF8(c, c_ascii); client.out_headers.Add(L"Content-Length", c_ascii.size()); CreateOutputHeaders(client); client.output_buffer += c_ascii; client.reading_to_buffer = 0; client.sending_from_buffer = 1; client.answering_to_client = true; } return; } /* else { client.status = 200; client.out_headers.Add(L"Content-Type", L"text/html; charset=UTF-8"); c = L"hello world from my webserver, your requested: " + client.url; c += L"\r\n"; if( client.url == L"/quit" ) { c += L"

bye bye"; close_server = true; } PT::WideToUTF8(c,c_ascii); client.out_headers.Add(L"Content-Length", c_ascii.size()); CreateOutputHeaders(client); client.output_buffer += c_ascii; client.reading_to_buffer = 0; client.sending_from_buffer = 1; client.answering_to_client = true; } */ }