1037 lines
19 KiB
C++
1037 lines
19 KiB
C++
#include <iostream>
|
|
#include <string.h>
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/select.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <space/space.h>
|
|
#include <utf8/utf8.h>
|
|
|
|
|
|
|
|
#define WEBSERVER_INPUT_BUFFER_MAX_LEN 8196
|
|
//#define WEBSERVER_INPUT_BUFFER_MAX_LEN 2
|
|
|
|
|
|
enum HTTPMethod
|
|
{
|
|
http_method_get = 0,
|
|
http_method_post,
|
|
http_method_put,
|
|
http_method_delete,
|
|
http_method_options,
|
|
http_method_unsupported
|
|
};
|
|
|
|
|
|
enum HTTPVersion
|
|
{
|
|
http_version_1_0,
|
|
http_version_1_1,
|
|
http_version_unsupported
|
|
};
|
|
|
|
|
|
class Client
|
|
{
|
|
public:
|
|
|
|
int socket;
|
|
PT::Space in;
|
|
|
|
std::wstring url;
|
|
HTTPMethod http_method;
|
|
HTTPVersion http_version;
|
|
|
|
bool answer_generated;
|
|
|
|
bool close_connection;
|
|
|
|
|
|
char * in_buffer; // rename to a better name
|
|
size_t in_buffer_max_len; // at least two characters
|
|
size_t in_buffer_len;
|
|
|
|
std::string input_buffer;
|
|
std::string header;
|
|
|
|
bool parsing_headers;
|
|
bool parsing_first_header;
|
|
|
|
|
|
std::string output_buffer;
|
|
size_t output_buffer_sent; // how many characters were sent
|
|
|
|
|
|
Client() : in_buffer_max_len(WEBSERVER_INPUT_BUFFER_MAX_LEN) // put as a constant somewhere
|
|
{
|
|
socket = 0;
|
|
in_buffer = nullptr;
|
|
in_buffer_len = 0;
|
|
parsing_headers = true;
|
|
parsing_first_header = true;
|
|
http_method = http_method_unsupported;
|
|
http_version = http_version_unsupported;
|
|
answer_generated = false;
|
|
output_buffer_sent = 0;
|
|
close_connection = false;
|
|
}
|
|
|
|
|
|
Client(const Client & c)
|
|
{
|
|
in_buffer = nullptr;
|
|
operator=(c);
|
|
}
|
|
|
|
|
|
Client & operator=(const Client & c)
|
|
{
|
|
socket = c.socket;
|
|
|
|
if( c.in_buffer )
|
|
AllocInputBuffer();
|
|
else
|
|
in_buffer = nullptr;
|
|
|
|
in_buffer_len = c.in_buffer_len;
|
|
in_buffer_max_len = c.in_buffer_max_len;
|
|
parsing_headers = c.parsing_headers;
|
|
parsing_first_header = c.parsing_first_header;
|
|
http_method = c.http_method;
|
|
http_version = c.http_version;
|
|
url = c.url;
|
|
answer_generated = c.answer_generated;
|
|
|
|
output_buffer = c.output_buffer;
|
|
output_buffer_sent = c.output_buffer_sent;
|
|
close_connection = c.close_connection;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
~Client()
|
|
{
|
|
delete [] in_buffer;
|
|
}
|
|
|
|
void AllocInputBuffer()
|
|
{
|
|
delete [] in_buffer;
|
|
in_buffer = new char[in_buffer_max_len];
|
|
}
|
|
|
|
|
|
void PrepareToNewRequest()
|
|
{
|
|
if( !in_buffer )
|
|
AllocInputBuffer();
|
|
|
|
parsing_headers = true;
|
|
parsing_first_header = true;
|
|
in_buffer_len = 0;
|
|
http_method = http_method_unsupported;
|
|
http_version = http_version_unsupported;
|
|
answer_generated = false;
|
|
output_buffer_sent = 0;
|
|
close_connection = false;
|
|
|
|
output_buffer.clear();
|
|
url.clear();
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
class Server
|
|
{
|
|
public:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Server()
|
|
{
|
|
main_socket = -1;
|
|
close_server = false;
|
|
|
|
}
|
|
|
|
virtual ~Server()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
|
|
void CloseMainSocket()
|
|
{
|
|
if( main_socket != -1 )
|
|
{
|
|
close(main_socket);
|
|
main_socket = -1;
|
|
}
|
|
}
|
|
|
|
|
|
void CloseClientSockets()
|
|
{
|
|
for(size_t i=0 ; i<client_tab.size() ; ++i)
|
|
close(client_tab[i].socket);
|
|
|
|
client_tab.clear();
|
|
}
|
|
|
|
|
|
void Close()
|
|
{
|
|
CloseMainSocket();
|
|
CloseClientSockets();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void 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 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
|
|
{
|
|
ReadInputFromClients();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::vector<Client> client_tab;
|
|
int main_socket;
|
|
fd_set read_set, write_set;
|
|
std::wstring tmp_header, tmp_value;
|
|
size_t header_index;
|
|
std::string url_ascii;
|
|
bool close_server;
|
|
|
|
|
|
int 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 )
|
|
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 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 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 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 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 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 IsHeadersEnding(const char * ptr)
|
|
{
|
|
return ptr[0] == '\r' && ptr[0+1] == '\n' &&
|
|
ptr[0+2] == '\r' && ptr[0+3] == '\n';
|
|
}
|
|
|
|
|
|
void 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 ParseFirstHeader(Client & client)
|
|
{
|
|
return ParseFirstHeaderMethodName(client) &&
|
|
ParseFirstHeaderURL(client) &&
|
|
ParseFirstHeaderHTTPVersion(client);
|
|
}
|
|
|
|
|
|
bool 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);
|
|
}
|
|
|
|
|
|
virtual bool 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 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<wchar_t>((v1 << 4) + v2);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
if( c < 32 || c > 127 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
url_ascii += static_cast<char>(c);
|
|
}
|
|
|
|
bool utf8_correct = PT::UTF8ToWide(url_ascii, client.url);
|
|
|
|
SkipWhite(client);
|
|
url_ascii.clear();
|
|
|
|
return utf8_correct && !client.url.empty();
|
|
}
|
|
|
|
|
|
|
|
bool 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 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 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 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 ReadInputFromClients we have a loop through client_tab
|
|
// // (next item will be skipped by ++i)
|
|
// break;
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
|
|
bool IsDecDigit(wchar_t c)
|
|
{
|
|
if( c >= '0' && c <= '9' )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool IsHexDigit(wchar_t c)
|
|
{
|
|
if( c >= '0' && c <= '9' )
|
|
return true;
|
|
|
|
if( ToLower(c) >= 'a' && ToLower(c) <= 'f' )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
int HexDigitToValue(wchar_t c)
|
|
{
|
|
if( c >= '0' && c <= '9' )
|
|
return static_cast<int>(c - '0');
|
|
|
|
if( ToLower(c) >= 'a' && ToLower(c) <= 'f' )
|
|
return static_cast<int>(c - 'a') + 10;
|
|
|
|
return 0;
|
|
}
|
|
|
|
wchar_t ToLower(wchar_t c)
|
|
{
|
|
if( c >= 'A' && c <= 'Z' )
|
|
c = c - 'A' + 'a';
|
|
|
|
return c;
|
|
}
|
|
|
|
|
|
bool 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 CompareNoCase(const std::wstring & str1, const wchar_t * str2)
|
|
{
|
|
return CompareNoCase(str1.c_str(), str2);
|
|
}
|
|
|
|
|
|
|
|
// do not use \r or \n here
|
|
bool IsWhite(wchar_t c)
|
|
{
|
|
return (c == ' ' || c == '\t');
|
|
}
|
|
|
|
|
|
void 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 SkipWhite(Client & client)
|
|
{
|
|
while( header_index < client.input_buffer.size() &&
|
|
IsWhite(client.input_buffer[header_index]) )
|
|
{
|
|
header_index += 1;
|
|
}
|
|
}
|
|
|
|
|
|
virtual void 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"<br><br>bye bye";
|
|
close_server = true;
|
|
}
|
|
|
|
PT::WideToUTF8(a, client.output_buffer);
|
|
}
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
Server server;
|
|
|
|
|
|
void signal_handler_sigpipe(int)
|
|
{
|
|
std::cout << "SIGPIPE we are continuing" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void signal_handler(int s)
|
|
{
|
|
server.Close();
|
|
|
|
|
|
std::cout << "bye bye (signal caught)" << std::endl;
|
|
std::exit(0);
|
|
}
|
|
|
|
|
|
|
|
int main()
|
|
{
|
|
signal(SIGTERM, signal_handler);
|
|
signal(SIGINT, signal_handler);
|
|
|
|
signal(SIGPIPE, signal_handler_sigpipe);
|
|
|
|
|
|
server.PrepareMainSocket();
|
|
server.Wait();
|
|
|
|
|
|
}
|
|
|
|
|
|
|