some code from Parser moved to HeadersParser and to string_functions
git-svn-id: svn://ttmath.org/publicrep/libscorpiohttpserver/trunk@1056 e52654a7-88a9-db11-a3e9-0013d4bc506e
This commit is contained in:
parent
c3afe35119
commit
103f4cbc12
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
CXX = g++5
|
CXX = g++5
|
||||||
CXXFLAGS = -g3 -O0 -I/usr/local/include -std=c++11 -I../../pikotools
|
CXXFLAGS = -g3 -O0 -std=c++11 -pedantic -Wall -I/usr/local/include -I../../pikotools
|
||||||
o=$(patsubst %.cpp,%.o,$(wildcard *.cpp))
|
o=$(patsubst %.cpp,%.o,$(wildcard *.cpp))
|
||||||
name=webserver
|
name=webserver
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ webserver: $(o)
|
||||||
clean:
|
clean:
|
||||||
rm -f $(name)
|
rm -f $(name)
|
||||||
|
|
||||||
|
|
||||||
cleanall: clean
|
cleanall: clean
|
||||||
@cd ../../pikotools && make clean
|
@cd ../../pikotools && make clean
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
# DO NOT DELETE
|
# DO NOT DELETE
|
||||||
|
|
||||||
client.o: client.h
|
client.o: client.h
|
||||||
main.o: ./server.h client.h
|
headers_parser.o: headers_parser.h client.h string_functions.h
|
||||||
server.o: ./server.h client.h
|
main.o: server.h client.h headers_parser.h
|
||||||
|
server.o: server.h client.h headers_parser.h string_functions.h
|
||||||
|
string_functions.o: string_functions.h
|
||||||
|
|
|
@ -45,6 +45,7 @@ Client & Client::operator=(const Client & c)
|
||||||
in_buffer_max_len = c.in_buffer_max_len;
|
in_buffer_max_len = c.in_buffer_max_len;
|
||||||
parsing_headers = c.parsing_headers;
|
parsing_headers = c.parsing_headers;
|
||||||
parsing_first_header = c.parsing_first_header;
|
parsing_first_header = c.parsing_first_header;
|
||||||
|
http_method_str = c.http_method_str;
|
||||||
http_method = c.http_method;
|
http_method = c.http_method;
|
||||||
http_version = c.http_version;
|
http_version = c.http_version;
|
||||||
url = c.url;
|
url = c.url;
|
||||||
|
@ -81,6 +82,7 @@ void Client::PrepareToNewRequest()
|
||||||
output_buffer_sent = 0;
|
output_buffer_sent = 0;
|
||||||
close_connection = false;
|
close_connection = false;
|
||||||
|
|
||||||
|
http_method_str.clear();
|
||||||
output_buffer.clear();
|
output_buffer.clear();
|
||||||
url.clear();
|
url.clear();
|
||||||
}
|
}
|
||||||
|
|
15
src/client.h
15
src/client.h
|
@ -45,11 +45,6 @@ public:
|
||||||
|
|
||||||
bool close_connection;
|
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 input_buffer;
|
||||||
std::string header;
|
std::string header;
|
||||||
|
|
||||||
|
@ -71,6 +66,16 @@ public:
|
||||||
void PrepareToNewRequest();
|
void PrepareToNewRequest();
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
char * in_buffer; // rename to a better name
|
||||||
|
size_t in_buffer_max_len; // at least two characters
|
||||||
|
size_t in_buffer_len;
|
||||||
|
std::wstring http_method_str;
|
||||||
|
|
||||||
|
|
||||||
|
friend class Server;
|
||||||
|
friend class HeadersParser;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,301 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <utf8/utf8.h>
|
||||||
|
#include "headers_parser.h"
|
||||||
|
#include "string_functions.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void HeadersParser::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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ptr buffer should consists of at least more 3 characters
|
||||||
|
bool HeadersParser::IsHeadersEnding(const char * ptr)
|
||||||
|
{
|
||||||
|
return ptr[0] == '\r' && ptr[0+1] == '\n' &&
|
||||||
|
ptr[0+2] == '\r' && ptr[0+3] == '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool HeadersParser::ParseFirstHeader(Client & client)
|
||||||
|
{
|
||||||
|
return ParseFirstHeaderMethodName(client) &&
|
||||||
|
ParseFirstHeaderURL(client) &&
|
||||||
|
ParseFirstHeaderHTTPVersion(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool HeadersParser::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;
|
||||||
|
client.http_method_str = method_name;
|
||||||
|
SkipWhite(client);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 HeadersParser::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 HeadersParser::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 HeadersParser::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 HeadersParser::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 HeadersParser::SkipWhite(Client & client)
|
||||||
|
{
|
||||||
|
while( header_index < client.input_buffer.size() &&
|
||||||
|
IsWhite(client.input_buffer[header_index]) )
|
||||||
|
{
|
||||||
|
header_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef headerfile_libscorpiohttpserver_src_headers_parser_h
|
||||||
|
#define headerfile_libscorpiohttpserver_src_headers_parser_h
|
||||||
|
|
||||||
|
#include "client.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class HeadersParser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
void ParseHeaders(Client & client);
|
||||||
|
bool IsHeadersEnding(const char * ptr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
size_t header_index;
|
||||||
|
std::wstring tmp_header, tmp_value;
|
||||||
|
std::string url_ascii;
|
||||||
|
|
||||||
|
bool ParseFirstHeader(Client & client);
|
||||||
|
bool ParseFirstHeaderMethodName(Client & client);
|
||||||
|
bool ParseFirstHeaderURL(Client & client);
|
||||||
|
bool ParseFirstHeaderHTTPVersion(Client & client);
|
||||||
|
bool ParseHeaderKey(Client & client);
|
||||||
|
void ParseHeaderValue(Client & client);
|
||||||
|
|
||||||
|
void SkipWhite(Client & client);
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
392
src/server.cpp
392
src/server.cpp
|
@ -8,7 +8,7 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <utf8/utf8.h>
|
#include <utf8/utf8.h>
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
#include "string_functions.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ Server::Server()
|
||||||
{
|
{
|
||||||
main_socket = -1;
|
main_socket = -1;
|
||||||
close_server = false;
|
close_server = false;
|
||||||
header_index = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Server::~Server()
|
Server::~Server()
|
||||||
|
@ -124,7 +123,7 @@ void Server::Wait()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ReadInputFromClients();
|
ReadWriteToClients();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,8 +206,7 @@ int optval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// change to a better name as it is used to write to a client too
|
void Server::ReadWriteToClients()
|
||||||
void Server::ReadInputFromClients()
|
|
||||||
{
|
{
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
|
||||||
|
@ -257,6 +255,8 @@ void Server::ReadInputFromClient(Client & client)
|
||||||
|
|
||||||
char * old_pointer = client.in_buffer + client.in_buffer_len;
|
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);
|
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 )
|
if( read_len < 0 )
|
||||||
|
@ -265,8 +265,6 @@ void Server::ReadInputFromClient(Client & client)
|
||||||
|
|
||||||
std::cout << "read failed" << std::endl;
|
std::cout << "read failed" << std::endl;
|
||||||
client.close_connection = true;
|
client.close_connection = true;
|
||||||
//RemoveClientSocket(client.socket);
|
|
||||||
// do not use client reference anymore
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if( read_len == 0 )
|
if( read_len == 0 )
|
||||||
|
@ -354,9 +352,16 @@ void Server::CheckHeaders(Client & client, size_t input_buffer_index)
|
||||||
|
|
||||||
for( ; input_buffer_index + 3 < client.input_buffer.size() ; ++input_buffer_index)
|
for( ; input_buffer_index + 3 < client.input_buffer.size() ; ++input_buffer_index)
|
||||||
{
|
{
|
||||||
if( IsHeadersEnding(client.input_buffer.c_str() + input_buffer_index) )
|
if( headers_parser.IsHeadersEnding(client.input_buffer.c_str() + input_buffer_index) )
|
||||||
{
|
{
|
||||||
ParseHeaders(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;
|
client.parsing_headers = false;
|
||||||
client.input_buffer.erase(0, input_buffer_index + 4);
|
client.input_buffer.erase(0, input_buffer_index + 4);
|
||||||
|
|
||||||
|
@ -377,87 +382,11 @@ void Server::CheckHeaders(Client & client, size_t input_buffer_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 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)
|
bool Server::SelectMethodName(Client & client, const std::wstring & method_name)
|
||||||
{
|
|
||||||
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;
|
client.http_method = http_method_unsupported;
|
||||||
|
|
||||||
|
@ -490,202 +419,9 @@ bool Server::SelectMethodName(Client & client, wchar_t * method_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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<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 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)
|
//void Server::RemoveClientSocket(int client_socket)
|
||||||
//{
|
//{
|
||||||
|
@ -696,7 +432,7 @@ void Server::ParseHeaderValue(Client & client)
|
||||||
// close(client_socket);
|
// close(client_socket);
|
||||||
// client_tab.erase(client_tab.begin() + i);
|
// client_tab.erase(client_tab.begin() + i);
|
||||||
//
|
//
|
||||||
// // warning: in ReadInputFromClients we have a loop through client_tab
|
// // warning: in ReadWriteToClients we have a loop through client_tab
|
||||||
// // (next item will be skipped by ++i)
|
// // (next item will be skipped by ++i)
|
||||||
// break;
|
// break;
|
||||||
// }
|
// }
|
||||||
|
@ -704,101 +440,6 @@ void Server::ParseHeaderValue(Client & client)
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
|
||||||
bool Server::IsDecDigit(wchar_t c)
|
|
||||||
{
|
|
||||||
if( c >= '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<int>(c - '0');
|
|
||||||
|
|
||||||
if( ToLower(c) >= 'a' && ToLower(c) <= 'f' )
|
|
||||||
return static_cast<int>(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)
|
void Server::CreateAnswer(Client & client)
|
||||||
|
@ -809,6 +450,7 @@ void Server::CreateAnswer(Client & client)
|
||||||
a += L"Content-Type: text/html; charset=UTF-8\r\n";
|
a += L"Content-Type: text/html; charset=UTF-8\r\n";
|
||||||
a += L"\r\n";
|
a += L"\r\n";
|
||||||
a += L"hello world from my webserwer, your requested: " + client.url;
|
a += L"hello world from my webserwer, your requested: " + client.url;
|
||||||
|
a += L"\r\n";
|
||||||
|
|
||||||
if( client.url == L"/quit" )
|
if( client.url == L"/quit" )
|
||||||
{
|
{
|
||||||
|
|
56
src/server.h
56
src/server.h
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
|
#include "headers_parser.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,74 +31,25 @@ private:
|
||||||
std::vector<Client> client_tab;
|
std::vector<Client> client_tab;
|
||||||
int main_socket;
|
int main_socket;
|
||||||
fd_set read_set, write_set;
|
fd_set read_set, write_set;
|
||||||
std::wstring tmp_header, tmp_value;
|
|
||||||
size_t header_index;
|
|
||||||
std::string url_ascii;
|
|
||||||
bool close_server;
|
bool close_server;
|
||||||
|
HeadersParser headers_parser;
|
||||||
|
|
||||||
|
|
||||||
int AddSocketsToSet();
|
int AddSocketsToSet();
|
||||||
void AddNewClient();
|
void AddNewClient();
|
||||||
|
|
||||||
// change to a better name as it is used to write to a client too
|
void ReadWriteToClients();
|
||||||
void ReadInputFromClients();
|
|
||||||
|
|
||||||
void ReadInputFromClient(Client & client);
|
void ReadInputFromClient(Client & client);
|
||||||
void WriteOutputToClient(Client & client);
|
void WriteOutputToClient(Client & client);
|
||||||
|
|
||||||
void CheckHeaders(Client & client, size_t input_buffer_index);
|
void CheckHeaders(Client & client, size_t input_buffer_index);
|
||||||
|
|
||||||
|
|
||||||
// ptr buffer should consists of at least more 3 characters
|
virtual bool SelectMethodName(Client & client, const std::wstring & method_name);
|
||||||
bool IsHeadersEnding(const char * ptr);
|
|
||||||
|
|
||||||
void ParseHeaders(Client & client);
|
|
||||||
bool ParseFirstHeader(Client & client);
|
|
||||||
|
|
||||||
|
|
||||||
bool ParseFirstHeaderMethodName(Client & client);
|
|
||||||
|
|
||||||
|
|
||||||
virtual bool SelectMethodName(Client & client, wchar_t * method_name);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
bool ParseFirstHeaderHTTPVersion(Client & client);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ParseHeaderKey should increment header_index at least once
|
|
||||||
bool ParseHeaderKey(Client & client);
|
|
||||||
|
|
||||||
|
|
||||||
void ParseHeaderValue(Client & client);
|
|
||||||
|
|
||||||
|
|
||||||
//void RemoveClientSocket(int client_socket);
|
//void RemoveClientSocket(int client_socket);
|
||||||
|
|
||||||
|
|
||||||
bool IsDecDigit(wchar_t c);
|
|
||||||
bool IsHexDigit(wchar_t c);
|
|
||||||
int HexDigitToValue(wchar_t c);
|
|
||||||
wchar_t ToLower(wchar_t c);
|
|
||||||
bool CompareNoCase(const wchar_t * str1, const wchar_t * str2);
|
|
||||||
bool CompareNoCase(const std::wstring & str1, const wchar_t * str2);
|
|
||||||
|
|
||||||
// do not use \r or \n here
|
|
||||||
bool IsWhite(wchar_t c);
|
|
||||||
|
|
||||||
void TrimWhiteAtEnd(std::wstring & str);
|
|
||||||
void SkipWhite(Client & client);
|
|
||||||
virtual void CreateAnswer(Client & client);
|
virtual void CreateAnswer(Client & client);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
#include <utf8/utf8.h>
|
||||||
|
#include "string_functions.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef headerfile_libscorpiohttpserver_src_string_functions_h
|
||||||
|
#define headerfile_libscorpiohttpserver_src_string_functions_h
|
||||||
|
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
wchar_t ToLower(wchar_t c);
|
||||||
|
|
||||||
|
bool CompareNoCase(const wchar_t * str1, const wchar_t * str2);
|
||||||
|
bool CompareNoCase(const std::wstring & str1, const wchar_t * str2);
|
||||||
|
|
||||||
|
bool IsDecDigit(wchar_t c);
|
||||||
|
bool IsHexDigit(wchar_t c);
|
||||||
|
int HexDigitToValue(wchar_t c);
|
||||||
|
|
||||||
|
bool IsWhite(wchar_t c);
|
||||||
|
void TrimWhiteAtEnd(std::wstring & str);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue