495 lines
9.2 KiB
C++
495 lines
9.2 KiB
C++
#include <iostream>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
#include <netinet/in.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <utf8/utf8.h>
|
|
#include "server.h"
|
|
#include "string_functions.h"
|
|
|
|
|
|
|
|
Server::Server()
|
|
{
|
|
main_socket = -1;
|
|
close_server = false;
|
|
}
|
|
|
|
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<client_tab.size() ; ++i)
|
|
close(client_tab[i].socket);
|
|
|
|
client_tab.clear();
|
|
}
|
|
|
|
|
|
void Server::Close()
|
|
{
|
|
CloseMainSocket();
|
|
CloseClientSockets();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 )
|
|
{
|
|
int fd_max = AddSocketsToSet();
|
|
int ready_fd = select(fd_max + 1, &read_set, &write_set, 0, 0);
|
|
|
|
if( ready_fd < 0 )
|
|
{
|
|
std::cout << "select failed" << std::endl;
|
|
return;
|
|
}
|
|
|
|
if( ready_fd > 0 )
|
|
{
|
|
if( FD_ISSET(main_socket, &read_set) )
|
|
{
|
|
AddNewClient();
|
|
}
|
|
else
|
|
{
|
|
ReadWriteToClients();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
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<client_tab.size() ; ++i)
|
|
{
|
|
FD_SET(client_tab[i].socket, &read_set);
|
|
|
|
if( client_tab[i].answer_generated && !client_tab[i].output_buffer.empty() )
|
|
FD_SET(client_tab[i].socket, &write_set);
|
|
|
|
if( client_tab[i].socket > 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;
|
|
c.close_connection = false;
|
|
|
|
client_tab.push_back(c);
|
|
client_tab.back().PrepareToNewRequest();
|
|
}
|
|
else
|
|
{
|
|
std::cout << "I cannot fcntl (O_NONBLOCK)" << std::endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Server::ReadWriteToClients()
|
|
{
|
|
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;
|
|
|
|
// after adding to client.input_buffer we set in_buffer_len to 0
|
|
// so here is sufficient client.in_buffer (without adding 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;
|
|
}
|
|
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;
|
|
|
|
// the client has terminated connection
|
|
client.close_connection = true;
|
|
}
|
|
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.PrepareToNewRequest();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
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( headers_parser.IsHeadersEnding(client.input_buffer.c_str() + input_buffer_index) )
|
|
{
|
|
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;
|
|
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;
|
|
|
|
if( client.http_version == http_version_1_0 )
|
|
{
|
|
std::cout << "answer generated, closing connection for http 1.0" << std::endl;
|
|
client.close_connection = true;
|
|
}
|
|
}
|
|
|
|
if( client.http_method != http_method_get )
|
|
{
|
|
// at the moment only get method
|
|
client.close_connection = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 ; i<client_tab.size() ; ++i)
|
|
// {
|
|
// if( client_tab[i].socket == client_socket )
|
|
// {
|
|
// close(client_socket);
|
|
// client_tab.erase(client_tab.begin() + i);
|
|
//
|
|
// // warning: in ReadWriteToClients we have a loop through client_tab
|
|
// // (next item will be skipped by ++i)
|
|
// break;
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
|
|
|
|
|
|
void Server::CreateAnswer(Client & client)
|
|
{
|
|
std::wstring a, c;
|
|
|
|
c = L"hello world from my webserver, your requested: " + client.url;
|
|
c += L"\r\n";
|
|
|
|
if( client.url == L"/quit" )
|
|
{
|
|
c += L"<br><br>bye bye";
|
|
close_server = true;
|
|
}
|
|
|
|
std::string c_ascii;
|
|
PT::WideToUTF8(c,c_ascii);
|
|
|
|
|
|
a = L"HTTP/";
|
|
|
|
if( client.http_version == http_version_1_0 )
|
|
a += L"1.0";
|
|
else
|
|
a += L"1.1";
|
|
|
|
a += L" 200 OK\r\n";
|
|
a += L"Content-Type: text/html; charset=UTF-8\r\n";
|
|
|
|
|
|
|
|
wchar_t buf[32];
|
|
swprintf(buf, sizeof(buf)/sizeof(wchar_t), L"%d", c_ascii.size());
|
|
|
|
a += L"Content-Length: ";
|
|
a += buf;
|
|
a += L"\r\n";
|
|
a += L"\r\n";
|
|
|
|
|
|
PT::WideToUTF8(a, client.output_buffer);
|
|
client.output_buffer += c_ascii;
|
|
}
|
|
|
|
|