removed: Request::debug all stream used for debugging info

some environment variables were put there
removed: config variable: debug_info
removed: Request::role (responder, authorizer)
         now we have only one role: responder
added:   new config variables:
         log_env_variables (default false) - when true then fastcgi environment
         variables are logged to the log file
         log_http_answer_headers (default false) - when true all http headers
         created by winix ale logged (note that the www server can add/adjust other headers)
changed: some refactoring in Request struct
changed: CookieTab to std::map<std::wstring, std::wstring>
         beforehand std::string was used
         (changed CookieParser as well)
changed: Request::SetCookie() to AddCookie()
added:   Request::out_headers (a PT::Space struct)
         http headers (without cookies) send back to the client
added:   Request::out_cookies (a PT::Space struct)
         cookies send to the client
changed: App class to use Request::out_headers and Request::out_cookies
         some SendHeaders...() methods were renamed to PrepareHeaders...()
         and they create output in Request::out_headers first (and out_cookies)
         and later it is sent
added:   two plugin messages:
         // http headers (without cookies) were created and are ready to send
         // here you can make some changes to them
         // in p1 you have a pointer to the PT::Space (Request::out_headers)
         #define WINIX_PREPARE_TO_SEND_HTTP_HEADERS		31070

         // http cookies were created and are ready to send
         // here you can make some changes to them
         // in p1 you have a pointer to the PT::Space (Request::out_cookies)
         #define WINIX_PREPARE_TO_SEND_HTTP_COOKIES		31080
added:   config variable:
         // how many output streams do we have in Request class
         // default: 16 (64 maximum)
         size_t ezc_out_streams_size;




git-svn-id: svn://ttmath.org/publicrep/winix/trunk@940 e52654a7-88a9-db11-a3e9-0013d4bc506e
This commit is contained in:
Tomasz Sowa 2013-11-29 21:39:23 +00:00
parent 3e32f3784f
commit 375604edd6
11 changed files with 419 additions and 302 deletions

View File

@ -162,6 +162,8 @@ bool App::Init()
post_parser.LogValueSize(config.log_post_value_size); post_parser.LogValueSize(config.log_post_value_size);
// post_multi_parser has a pointer to the config // post_multi_parser has a pointer to the config
cookie_parser.UTF8(config.utf8);
plugin.Call((Session*)0, WINIX_PLUGIN_INIT); plugin.Call((Session*)0, WINIX_PLUGIN_INIT);
return true; return true;
@ -572,26 +574,13 @@ void App::Make()
plugin.Call(WINIX_CONTENT_MAKE); plugin.Call(WINIX_CONTENT_MAKE);
MakePage(); MakePage();
if( config.debug_info )
{
// !! dodac inne informacje (get, post, itp)
// jesli jest debug_info wlaczone to nie robic przekierowan
PrintEnv();
}
} }
void App::PrintEnv() void App::LogEnvironmentVariables()
{ {
char ** e; for(char ** e = fcgi_request.envp ; *e ; ++e )
log << log1 << "Env: " << *e << logend;
cur.request->debug << "environment variables:\n";
for( e = fcgi_request.envp ; *e ; ++e )
cur.request->debug << ' ' << *e << "\n";
cur.request->debug << '\n';
} }
@ -601,7 +590,6 @@ void App::ReadRequest()
{ {
ReadEnvVariables(); ReadEnvVariables();
CheckRequestMethod(); CheckRequestMethod();
CheckFCGIRole();
CheckSSL(); CheckSSL();
SetSubdomain(); SetSubdomain();
@ -611,14 +599,14 @@ void App::ReadRequest()
cookie_parser.Parse(cur.request->env_http_cookie, cur.request->cookie_tab); cookie_parser.Parse(cur.request->env_http_cookie, cur.request->cookie_tab);
accept_encoding_parser.ParseAndLog(cur.request->env_http_accept_encoding); accept_encoding_parser.ParseAndLog(cur.request->env_http_accept_encoding);
if( config.log_env_variables )
LogEnvironmentVariables();
CheckIE(); CheckIE();
CheckKonqueror(); CheckKonqueror();
if( cur.request->using_ssl ) if( cur.request->using_ssl )
log << log3 << "App: connection secure through SSL" << logend; log << log3 << "App: connection secure through SSL" << logend;
if( cur.request->role == Request::authorizer )
log << log3 << "App: fast cgi role: authorizer" << logend;
} }
@ -658,7 +646,7 @@ void App::ReadEnvVariables()
void App::CheckRequestMethod() void App::CheckRequestMethod()
{ {
cur.request->method = Request::none; cur.request->method = Request::unknown_method;
if( ToSmall(cur.request->env_request_method[0]) == 'g' ) if( ToSmall(cur.request->env_request_method[0]) == 'g' )
cur.request->method = Request::get; cur.request->method = Request::get;
@ -671,15 +659,6 @@ void App::CheckRequestMethod()
} }
void App::CheckFCGIRole()
{
// default we assume 'responder'
cur.request->role = Request::responder;
if( ToSmall(cur.request->env_fcgi_role[0]) == 'a' )
cur.request->role = Request::authorizer;
}
void App::CheckSSL() void App::CheckSSL()
{ {
@ -774,22 +753,21 @@ void App::PrepareSessionCookie()
if( !cur.session->puser || !cur.session->remember_me ) if( !cur.session->puser || !cur.session->remember_me )
{ {
cur.request->SetCookie(config.http_session_id_name.c_str(), cur.session->id); cur.request->AddCookie(config.http_session_id_name, cur.session->id);
} }
else else
{ {
PT::Date expires = cur.request->start_time + config.session_remember_max_idle; PT::Date expires = cur.request->start_time + config.session_remember_max_idle;
cur.request->SetCookie(config.http_session_id_name.c_str(), cur.session->id, &expires); cur.request->AddCookie(config.http_session_id_name, cur.session->id, expires);
} }
} }
bool App::SendHeadersStaticCreateResource() bool App::PrepareHeadersStaticCreateResource(PT::WTextStream & out_path)
{ {
size_t i = 0; size_t i = 0;
Item * dir = system.dirs.GetDir(system.mounts.pmount->dir_id); Item * dir = system.dirs.GetDir(system.mounts.pmount->dir_id);
sendh_t3.clear();
if( !dir ) if( !dir )
{ {
@ -808,18 +786,18 @@ bool App::SendHeadersStaticCreateResource()
++i; ++i;
if( i > 0 ) if( i > 0 )
sendh_t3.assign(path, i); out_path.write(path, i);
return true; return true;
} }
void App::SendHeadersStatic() void App::PrepareHeadersStatic()
{ {
if( PathHasUpDir(cur.request->env_request_uri) ) if( PathHasUpDir(cur.request->env_request_uri) )
{ {
log << log1 << "App: incorrect path for a static file" << logend; log << log1 << "App: incorrect path for a static file" << logend;
SendHeadersForbidden(); PrepareHeadersForbidden();
return; return;
} }
@ -829,171 +807,213 @@ void App::SendHeadersStatic()
if( index >= config.static_dirs.size() ) if( index >= config.static_dirs.size() )
{ {
log << log1 << "App: static dir with index " << index << " is not defined in the config" << logend; log << log1 << "App: static dir with index " << index << " is not defined in the config" << logend;
SendHeadersForbidden(); PrepareHeadersForbidden();
return; return;
} }
PT::WideToUTF8(config.http_header_send_file, sendh_t); PT::WTextStream path;
PT::WideToUTF8(config.static_dirs[index], sendh_t2); path << config.static_dirs[index] << L"/";
if( !SendHeadersStaticCreateResource() ) if( !PrepareHeadersStaticCreateResource(path) )
{ {
SendHeadersForbidden(); PrepareHeadersForbidden();
return; return;
} }
FCGX_FPrintF(fcgi_request.out, "%s: %s/%s\r\n", sendh_t.c_str(), sendh_t2.c_str(), sendh_t3.c_str()); cur.request->out_headers.Add(config.http_header_send_file, path);
FCGX_PutS("Status: 200 OK\r\n", fcgi_request.out); cur.request->out_headers.Add(L"Status", L"200 OK");
log << log2 << "App: sending a file from a static mountpoint: " << sendh_t2 << "/" << sendh_t3 << logend; log << log2 << "App: sending a file from a static mountpoint: " << path << logend;
} }
void App::SendHeaderContentType() void App::PrepareHeaderContentType()
{ {
std::wstring * value = 0;
if( cur.request->return_json ) if( cur.request->return_json )
{ {
FCGX_PutS("Content-Type: application/json", fcgi_request.out); value = &cur.request->out_headers.Add(L"Content-Type", L"application/json");
} }
else else
{ {
switch( config.content_type_header ) switch( config.content_type_header )
{ {
case 1: case 1:
FCGX_PutS("Content-Type: application/xhtml+xml", fcgi_request.out); value = &cur.request->out_headers.Add(L"Content-Type", L"application/xhtml+xml");
break; break;
case 2: case 2:
FCGX_PutS("Content-Type: application/xml", fcgi_request.out); value = &cur.request->out_headers.Add(L"Content-Type", L"application/xml");
break; break;
case 0: case 0:
default: default:
FCGX_PutS("Content-Type: text/html", fcgi_request.out); value = &cur.request->out_headers.Add(L"Content-Type", L"text/html");
} }
} }
if( config.utf8 ) if( value && config.utf8 )
FCGX_PutS("; charset=UTF-8", fcgi_request.out); *value += L"; charset=UTF-8";
FCGX_PutS("\r\n", fcgi_request.out);
} }
void App::SendHeadersForbidden() void App::PrepareHeadersForbidden()
{ {
FCGX_PutS("Status: 403 Forbidden\r\n", fcgi_request.out); cur.request->out_headers.Add(L"Status", L"403 Forbidden");
SendHeaderContentType(); PrepareHeaderContentType();
} }
void App::SendHeadersRedirect() void App::PrepareHeadersRedirect()
{ {
switch(cur.request->redirect_type) switch(cur.request->redirect_type)
{ {
case 300: case 300:
FCGX_PutS("Status: 300 Multiple Choices\r\n", fcgi_request.out); cur.request->out_headers.Add(L"Status", L"300 Multiple Choices");
break; break;
case 301: case 301:
FCGX_PutS("Status: 301 Moved Permanently\r\n", fcgi_request.out); cur.request->out_headers.Add(L"Status", L"301 Moved Permanently");
break; break;
case 302: case 302:
FCGX_PutS("Status: 302 Found\r\n", fcgi_request.out); cur.request->out_headers.Add(L"Status", L"302 Found");
break; break;
case 307: case 307:
FCGX_PutS("Status: 307 Temporary Redirect\r\n", fcgi_request.out); cur.request->out_headers.Add(L"Status", L"307 Temporary Redirect");
break; break;
case 303: case 303:
default: default:
FCGX_PutS("Status: 303 See Other\r\n", fcgi_request.out); cur.request->out_headers.Add(L"Status", L"303 See Other");
break; break;
} }
PT::WideToUTF8(cur.request->redirect_to, cur.request->aredirect_to); cur.request->out_headers.Add(L"Location", cur.request->redirect_to);
FCGX_FPrintF(fcgi_request.out, "Location: %s\r\n", cur.request->aredirect_to.c_str()); log << log2 << "App: redirect to: " << cur.request->redirect_to << logend;
log << log2 << "App: redirect to: " << cur.request->aredirect_to << logend;
} }
void App::SendHeadersSendFile() void App::PrepareHeadersSendFile()
{ {
PT::WideToUTF8(config.http_header_send_file, sendfilea); cur.request->out_headers.Add(L"Status", L"200 OK");
PT::WideToUTF8(cur.request->x_sendfile, sendfile2a); cur.request->out_headers.Add(config.http_header_send_file, cur.request->x_sendfile);
FCGX_FPrintF(fcgi_request.out, "%s: %s\r\n", sendfilea.c_str(), sendfile2a.c_str());
FCGX_PutS("Status: 200 OK\r\n", fcgi_request.out);
log << log2 << "App: sending file: " << cur.request->x_sendfile << logend; log << log2 << "App: sending file: " << cur.request->x_sendfile << logend;
} }
void App::SendHeadersCompression(int compress_encoding) void App::PrepareHeadersCompression(int compress_encoding)
{ {
if( compress_encoding == 0 || compress_encoding == 1 ) if( compress_encoding == 0 || compress_encoding == 1 )
FCGX_PutS("Content-Encoding: deflate\r\n", fcgi_request.out); cur.request->out_headers.Add(L"Content-Encoding", L"deflate");
else else
FCGX_PutS("Content-Encoding: gzip\r\n", fcgi_request.out); cur.request->out_headers.Add(L"Content-Encoding", L"gzip");
} }
void App::SendHeadersNormal(Header header) void App::PrepareHeadersNormal(Header header)
{ {
switch( header ) switch( header )
{ {
case h_404: case h_404:
FCGX_PutS("Status: 404 Not Found\r\n", fcgi_request.out); cur.request->out_headers.Add(L"Status", L"404 Not Found");
SendHeaderContentType(); PrepareHeaderContentType();
break; break;
case h_403: case h_403:
SendHeadersForbidden(); PrepareHeadersForbidden();
break; break;
default: default:
FCGX_PutS("Status: 200 OK\r\n", fcgi_request.out); cur.request->out_headers.Add(L"Status", L"200 OK");
PrepareHeaderContentType();
if( cur.request->role != Request::authorizer )
SendHeaderContentType();
} }
} }
// we can improve SendHeaders and SendCookies methods by checking
// whether there is a new line character in either a name or a value
// and if such character exists and is being sent to the client it breaks the http headers and content
// and if compression is enabled the client's browser will not be able to decompress the stream
void App::SendHeaders()
{
PT::Space::TableSingle::iterator i;
PT::Space & headers = cur.request->out_headers;
void App::SendHeaders(bool compressing, int compress_encoding, Header header) plugin.Call(WINIX_PREPARE_TO_SEND_HTTP_HEADERS, &headers);
for(i=headers.table_single.begin() ; i != headers.table_single.end() ; ++i)
{
PT::WideToUTF8(i->first, aheader_name);
PT::WideToUTF8(i->second, aheader_value);
FCGX_PutS(aheader_name.c_str(), fcgi_request.out);
FCGX_PutS(": ", fcgi_request.out);
FCGX_PutS(aheader_value.c_str(), fcgi_request.out);
FCGX_PutS("\r\n", fcgi_request.out);
if( config.log_http_answer_headers )
log << "HTTP Header: " << aheader_name << ": " << aheader_value << logend;
}
}
void App::SendCookies()
{
PT::Space::TableSingle::iterator i;
PT::Space & cookies = cur.request->out_cookies;
plugin.Call(WINIX_PREPARE_TO_SEND_HTTP_COOKIES, &cookies);
for(i=cookies.table_single.begin() ; i != cookies.table_single.end() ; ++i)
{
PT::WideToUTF8(i->first, aheader_name);
PT::WideToUTF8(i->second, aheader_value);
FCGX_PutS("Set-Cookie: ", fcgi_request.out);
FCGX_PutS(aheader_name.c_str(), fcgi_request.out);
FCGX_PutS("=", fcgi_request.out);
FCGX_PutS(aheader_value.c_str(), fcgi_request.out);
FCGX_PutS("\r\n", fcgi_request.out);
if( config.log_http_answer_headers )
log << "HTTP Header: " << "Set-Cookie: " << aheader_name << "=" << aheader_value << logend;
}
}
void App::PrepareHeaders(bool compressing, int compress_encoding, Header header)
{ {
PrepareSessionCookie(); PrepareSessionCookie();
if( cur.request->send_as_attachment ) if( cur.request->send_as_attachment )
FCGX_PutS("Content-Disposition: attachment\r\n", fcgi_request.out); cur.request->out_headers.Add(L"Content-Disposition", L"attachment");
if( !cur.request->redirect_to.empty() ) if( !cur.request->redirect_to.empty() )
{ {
SendHeadersRedirect(); PrepareHeadersRedirect();
} }
else else
if( system.mounts.pmount->type == system.mounts.MountTypeStatic() ) if( system.mounts.pmount->type == system.mounts.MountTypeStatic() )
{ {
SendHeadersStatic(); PrepareHeadersStatic();
} }
else else
if( !cur.request->x_sendfile.empty() ) if( !cur.request->x_sendfile.empty() )
{ {
SendHeadersSendFile(); PrepareHeadersSendFile();
} }
else else
{ {
SendHeadersNormal(header); PrepareHeadersNormal(header);
} }
if( compressing ) if( compressing )
SendHeadersCompression(compress_encoding); PrepareHeadersCompression(compress_encoding);
FCGX_PutS(cur.request->headers.CStr(), fcgi_request.out);
FCGX_PutS("\r\n", fcgi_request.out);
} }
@ -1009,13 +1029,11 @@ void App::FilterCompressSend(bool compressing, int compress_encoding, const std:
if( config.html_filter && cur.request->use_html_filter && !raw && !cur.request->return_json ) if( config.html_filter && cur.request->use_html_filter && !raw && !cur.request->return_json )
{ {
TemplatesFunctions::html_filter.Filter(*source, clean_html); TemplatesFunctions::html_filter.Filter(*source, clean_html);
AddDebugInfo(clean_html);
source = &clean_html; source = &clean_html;
} }
else else
{ {
html_with_debug = *source; html_with_debug = *source;
AddDebugInfo(html_with_debug);
source = &html_with_debug; source = &html_with_debug;
} }
@ -1024,6 +1042,7 @@ void App::FilterCompressSend(bool compressing, int compress_encoding, const std:
else else
AssignString(*source, source_a); AssignString(*source, source_a);
// !! IMPROVE ME add to log the binary stream as well
if( config.log_server_answer ) if( config.log_server_answer )
log << log1 << "App: the server's answer is:\n" << source_a << "\nApp: end of the server's answer" << logend; log << log1 << "App: the server's answer is:\n" << source_a << "\nApp: end of the server's answer" << logend;
@ -1054,7 +1073,6 @@ void App::SelectCompression(size_t source_len, bool & compression_allowed, int &
compression_encoding = 0; compression_encoding = 0;
if( config.compression && if( config.compression &&
cur.request->role == Request::responder &&
cur.request->redirect_to.empty() && cur.request->redirect_to.empty() &&
cur.request->x_sendfile.empty() && cur.request->x_sendfile.empty() &&
!cur.request->browser_konqueror && /* !! sprawdzic czy Konqueror bedzie obslugiwal raw deflate */ !cur.request->browser_konqueror && /* !! sprawdzic czy Konqueror bedzie obslugiwal raw deflate */
@ -1102,10 +1120,6 @@ bool App::CanSendContent(Header header)
// if there is a redirect or a file to send then we do not send a content // if there is a redirect or a file to send then we do not send a content
return false; return false;
if( header == h_200 && cur.request->role == Request::authorizer && cur.request->is_item && cur.request->item.file_type != WINIX_ITEM_FILETYPE_NONE )
// if there is an item and the item has 'file' storage we do not send a content
return false;
/* /*
we don't have to check the HEAD method we don't have to check the HEAD method
the server (lighttpd) doesn't send the body of its own the server (lighttpd) doesn't send the body of its own
@ -1117,18 +1131,6 @@ return true;
} }
void App::AddDebugInfo(std::wstring & out)
{
if( config.debug_info )
{
if( !cur.request->debug.Empty() )
{
out += L"\n<!--\n";
out += cur.request->debug.Str();
out += L"\n-->\n";
}
}
}
@ -1165,7 +1167,10 @@ int compress_encoding;
header = h_200; header = h_200;
} }
SendHeaders(compressing, compress_encoding, header); PrepareHeaders(compressing, compress_encoding, header);
SendHeaders();
SendCookies();
FCGX_PutS("\r\n", fcgi_request.out);
if( CanSendContent(header) ) if( CanSendContent(header) )
{ {
@ -1193,7 +1198,7 @@ void App::SendData(const BinaryPage & page, FCGX_Stream * out)
send_data_buf[s] = *i; send_data_buf[s] = *i;
if( s > 0 ) if( s > 0 )
FCGX_PutStr(send_data_buf.c_str(), s, fcgi_request.out); FCGX_PutStr(send_data_buf.c_str(), s, out);
} }
} }
@ -1216,7 +1221,7 @@ int compress_encoding;
// !! IMPROVE ME add header: content-size // !! IMPROVE ME add header: content-size
SendHeaders(compressing, compress_encoding, header); PrepareHeaders(compressing, compress_encoding, header);
if( CanSendContent(header) ) if( CanSendContent(header) )
{ {
@ -1519,6 +1524,9 @@ void App::WaitForThreads()
void App::FetchPageOnExit() void App::FetchPageOnExit()
{ {
// !! CHANGE ME use curl instead of BSD's fetch...
// this allows to port to Linux systems
// stupid trick to break FCGX_Accept_r() function // stupid trick to break FCGX_Accept_r() function
// even with FCGX_InitRequest(..., ..., FCGI_FAIL_ACCEPT_ON_INTR) the FCGX_Accept_r // even with FCGX_InitRequest(..., ..., FCGI_FAIL_ACCEPT_ON_INTR) the FCGX_Accept_r
// doesn't want to break on a signal // doesn't want to break on a signal

View File

@ -119,12 +119,11 @@ private:
pthread_t signal_thread; pthread_t signal_thread;
std::string url_to_fetch_on_exit; std::string url_to_fetch_on_exit;
std::string source_a; std::string source_a;
std::string sendh_t, sendh_t2, sendh_t3;
std::string sendfilea, sendfile2a;
std::string send_data_buf; std::string send_data_buf;
PT::SpaceToJSON json_generic_serializer; PT::SpaceToJSON json_generic_serializer;
TextStream<std::wstring> json_out_stream; TextStream<std::wstring> json_out_stream;
BinaryPage out_bin_stream_compressed; BinaryPage out_bin_stream_compressed;
std::string aheader_name, aheader_value;
bool CheckAccessFromPlugins(); bool CheckAccessFromPlugins();
void ProcessRequestThrow(); void ProcessRequestThrow();
@ -146,7 +145,7 @@ private:
void SendBinaryAnswer(); void SendBinaryAnswer();
void SendAnswer(); void SendAnswer();
void PrintEnv(); void LogEnvironmentVariables();
void SetEnv(const char * & env, const char * name); void SetEnv(const char * & env, const char * name);
void ReadEnvVariables(); void ReadEnvVariables();
@ -155,23 +154,23 @@ private:
void CheckIE(); void CheckIE();
void CheckKonqueror(); void CheckKonqueror();
void CheckRequestMethod(); void CheckRequestMethod();
void CheckFCGIRole();
void CheckSSL(); void CheckSSL();
void SetSubdomain(); void SetSubdomain();
void PrepareSessionCookie(); void PrepareSessionCookie();
void AddDebugInfo(std::wstring & out);
void FilterCompressSend(bool compressing, int compress_encoding, const std::wstring & source_ref); void FilterCompressSend(bool compressing, int compress_encoding, const std::wstring & source_ref);
bool SendHeadersStaticCreateResource(); void SendHeaders();
void SendHeadersStatic(); void SendCookies();
void SendHeaderContentType(); bool PrepareHeadersStaticCreateResource(PT::WTextStream & out_path);
void SendHeadersForbidden(); void PrepareHeadersStatic();
void SendHeadersRedirect(); void PrepareHeaderContentType();
void SendHeadersSendFile(); void PrepareHeadersForbidden();
void SendHeadersCompression(int compress_encoding); void PrepareHeadersRedirect();
void SendHeadersNormal(Header header); void PrepareHeadersSendFile();
void SendHeaders(bool compressing, int compress_encoding, Header header); void PrepareHeadersCompression(int compress_encoding);
void PrepareHeadersNormal(Header header);
void PrepareHeaders(bool compressing, int compress_encoding, Header header);
int SelectDeflateVersion(); int SelectDeflateVersion();
void SelectCompression(size_t source_len, bool & compression_allowed, int & compression_encoding); void SelectCompression(size_t source_len, bool & compression_allowed, int & compression_encoding);
bool CanSendContent(Header header); bool CanSendContent(Header header);

View File

@ -118,6 +118,8 @@ void Config::AssignValues(bool stdout_is_closed)
log_db_query = Bool(L"log_db_query", false); log_db_query = Bool(L"log_db_query", false);
log_plugin_call = Bool(L"log_plugin_call", false); log_plugin_call = Bool(L"log_plugin_call", false);
log_post_value_size = Size(L"log_post_value_size", 80); log_post_value_size = Size(L"log_post_value_size", 80);
log_env_variables = Bool(L"log_env_variables", false);
log_http_answer_headers = Bool(L"log_http_answer_headers", false);
post_file_max = Size(L"post_file_max", 8388608); // 8 MB post_file_max = Size(L"post_file_max", 8388608); // 8 MB
@ -151,7 +153,7 @@ void Config::AssignValues(bool stdout_is_closed)
templates_index = Text(L"templates_index", L"index.html"); templates_index = Text(L"templates_index", L"index.html");
template_only_root_use_template_fun = Bool(L"template_only_root_use_template_fun", false); template_only_root_use_template_fun = Bool(L"template_only_root_use_template_fun", false);
http_session_id_name = AText(L"http_session_id_name"); http_session_id_name = Text(L"http_session_id_name", L"session_id");
db_database = AText(L"db_database"); db_database = AText(L"db_database");
db_user = AText(L"db_user"); db_user = AText(L"db_user");
db_pass = AText(L"db_pass"); db_pass = AText(L"db_pass");
@ -206,7 +208,6 @@ void Config::AssignValues(bool stdout_is_closed)
title_separator = Text(L"title_separator", L" / "); title_separator = Text(L"title_separator", L" / ");
http_header_send_file = Text(L"http_header_send_file", L"X-LIGHTTPD-send-file"); http_header_send_file = Text(L"http_header_send_file", L"X-LIGHTTPD-send-file");
debug_info = Bool(L"debug_info", false);
editors_html_safe_mode = Bool(L"editors_html_safe_mode", true); editors_html_safe_mode = Bool(L"editors_html_safe_mode", true);
editors_html_safe_mode_skip_root = Bool(L"editors_html_safe_mode_skip_root", true); editors_html_safe_mode_skip_root = Bool(L"editors_html_safe_mode_skip_root", true);
@ -237,6 +238,7 @@ void Config::AssignValues(bool stdout_is_closed)
ezc_max_elements = Size(L"ezc_max_elements", 50000); ezc_max_elements = Size(L"ezc_max_elements", 50000);
ezc_max_loop_elements = Size(L"ezc_max_loop_elements", 5000); ezc_max_loop_elements = Size(L"ezc_max_loop_elements", 5000);
ezc_out_streams_size = Size(L"ezc_out_streams_size", 16);
account_need_email_verification = Bool(L"account_need_email_verification", true); account_need_email_verification = Bool(L"account_need_email_verification", true);
reset_password_code_expiration_time = Long(L"reset_password_code_expiration_time", 86400); reset_password_code_expiration_time = Long(L"reset_password_code_expiration_time", 86400);
@ -259,6 +261,7 @@ void Config::AssignValues(bool stdout_is_closed)
incorrect_login_cannot_login_mode = Int(L"incorrect_login_cannot_login_mode", 0); incorrect_login_cannot_login_mode = Int(L"incorrect_login_cannot_login_mode", 0);
incorrect_login_cannot_login_treshold = Size(L"incorrect_login_cannot_login_treshold", 20); incorrect_login_cannot_login_treshold = Size(L"incorrect_login_cannot_login_treshold", 20);
incorrect_login_cannot_login_delay = Size(L"incorrect_login_cannot_login_delay", 1800); incorrect_login_cannot_login_delay = Size(L"incorrect_login_cannot_login_delay", 1800);
} }

View File

@ -91,6 +91,14 @@ public:
// request delimiter in the log file, default "---------" // request delimiter in the log file, default "---------"
std::wstring log_delimiter; std::wstring log_delimiter;
// log environment variables (fastcgi environment)
bool log_env_variables;
// log headers (+cookies) which are returned to the client
// this is what winix has generated -- the web server can change or add other headers
// default: false
bool log_http_answer_headers;
// fast cgi: socket (unix domain) // fast cgi: socket (unix domain)
std::string fcgi_socket; std::string fcgi_socket;
@ -128,7 +136,7 @@ public:
std::string db_user; std::string db_user;
std::string db_pass; std::string db_pass;
std::string http_session_id_name; std::wstring http_session_id_name;
// string used in a place where is a user (or group) selected // string used in a place where is a user (or group) selected
std::wstring priv_no_user; std::wstring priv_no_user;
@ -389,9 +397,6 @@ public:
// default: X-LIGHTTPD-send-file // default: X-LIGHTTPD-send-file
std::wstring http_header_send_file; std::wstring http_header_send_file;
// prints additional information (in the end of the html page as a commentary)
bool debug_info;
// in editors (emacs, ckeditor,...) the html will be filtered and unsafe tags // in editors (emacs, ckeditor,...) the html will be filtered and unsafe tags
// will be dropped (script, frame, etc.) // will be dropped (script, frame, etc.)
// default: true; // default: true;
@ -485,6 +490,10 @@ public:
// default: 5000 (from ezc generator) // default: 5000 (from ezc generator)
size_t ezc_max_loop_elements; size_t ezc_max_loop_elements;
// how many output streams do we have in Request class
// default: 16 (64 maximum)
size_t ezc_out_streams_size;
// when true then when a user want to create a new account // when true then when a user want to create a new account
// he has to provide his email and a message will be sent back to him // he has to provide his email and a message will be sent back to him
// with a link to activate the account // with a link to activate the account
@ -591,6 +600,7 @@ public:
Config(); Config();
bool ReadConfig(bool errors_to_stdout_, bool stdout_is_closed = true); bool ReadConfig(bool errors_to_stdout_, bool stdout_is_closed = true);

View File

@ -2,7 +2,7 @@
* This file is a part of Winix * This file is a part of Winix
* and is not publicly distributed * and is not publicly distributed
* *
* Copyright (c) 2008-2010, Tomasz Sowa * Copyright (c) 2008-2013, Tomasz Sowa
* All rights reserved. * All rights reserved.
* *
*/ */
@ -22,6 +22,8 @@ class CookieParser : public HttpSimpleParser
const char * cookie_string; const char * cookie_string;
CookieTab * cookie_tab; CookieTab * cookie_tab;
std::wstring temp_name, temp_value;
bool input_as_utf8;
protected: protected:
@ -35,19 +37,29 @@ protected:
return (int)(unsigned char)*(cookie_string++); return (int)(unsigned char)*(cookie_string++);
} }
void ConvStr(const std::string & src, std::wstring & dst)
{
if( input_as_utf8 )
PT::UTF8ToWide(src, dst);
else
AssignString(src, dst);
}
virtual void Parameter(std::string & name, std::string & value) virtual void Parameter(std::string & name, std::string & value)
{ {
// Cookie names are case insensitive according to section 3.1 of RFC 2965 // Cookie names are case insensitive according to section 3.1 of RFC 2965
// (we don't use locale here)
ToLower(name); ToLower(name);
std::pair<CookieTab::iterator, bool> res = cookie_tab->insert( std::make_pair(name, value) ); ConvStr(name, temp_name);
ConvStr(value, temp_value);
log << log2 << "Cookie, name: \"" << name << "\", value: \"" << value << "\""; std::pair<CookieTab::iterator, bool> res = cookie_tab->insert( std::make_pair(temp_name, temp_value) );
log << log2 << "Cookie, name: \"" << temp_name << "\", value: \"" << temp_value << "\"";
if( res.second == false ) if( res.second == false )
{ {
res.first->second = value; res.first->second = temp_value;
log << " (overwritten)"; log << " (overwritten)";
} }
@ -60,6 +72,7 @@ public:
CookieParser() CookieParser()
{ {
input_as_utf8 = false;
HttpSimpleParser::separator = ';'; HttpSimpleParser::separator = ';';
HttpSimpleParser::value_can_be_quoted = true; HttpSimpleParser::value_can_be_quoted = true;
HttpSimpleParser::skip_white_chars = true; HttpSimpleParser::skip_white_chars = true;
@ -67,6 +80,11 @@ public:
} }
void UTF8(bool utf)
{
input_as_utf8 = utf;
}
// cookie_string can be null // cookie_string can be null
void Parse(const char * cookie_string_, CookieTab & cookie_tab_) void Parse(const char * cookie_string_, CookieTab & cookie_tab_)
{ {

View File

@ -2,7 +2,7 @@
* This file is a part of Winix * This file is a part of Winix
* and is not publicly distributed * and is not publicly distributed
* *
* Copyright (c) 2008-2012, Tomasz Sowa * Copyright (c) 2008-2013, Tomasz Sowa
* All rights reserved. * All rights reserved.
* *
*/ */
@ -255,6 +255,15 @@
// to the resource // to the resource
#define WINIX_CHECK_PLUGIN_ACCESS 31060 #define WINIX_CHECK_PLUGIN_ACCESS 31060
// http headers (without cookies) were created and are ready to send
// here you can make some changes to them
// in p1 you have a pointer to the PT::Space (Request::out_headers)
#define WINIX_PREPARE_TO_SEND_HTTP_HEADERS 31070
// http cookies were created and are ready to send
// here you can make some changes to them
// in p1 you have a pointer to the PT::Space (Request::out_cookies)
#define WINIX_PREPARE_TO_SEND_HTTP_COOKIES 31080
/* /*

View File

@ -34,7 +34,12 @@ void Request::SetConfig(Config * pconfig)
void Request::ClearOutputStreams() void Request::ClearOutputStreams()
{ {
out_streams.resize(16); // !! IMPROVE ME add as a constant somewhere size_t len = config->ezc_out_streams_size;
if( len < 1 || len > 64 )
len = 16;
out_streams.resize(len);
for(size_t i=0 ; i<out_streams.size() ; ++i) for(size_t i=0 ; i<out_streams.size() ; ++i)
out_streams[i].Clear(); out_streams[i].Clear();
@ -54,13 +59,10 @@ void Request::Clear()
post_file_tab.clear(); post_file_tab.clear();
cookie_tab.clear(); cookie_tab.clear();
method = none; method = unknown_method;
role = responder;
headers.Clear(); out_headers.Clear();
out_cookies.Clear();
debug.Clear();
page_generated = false; page_generated = false;
use_html_filter = true; use_html_filter = true;
@ -81,7 +83,7 @@ void Request::Clear()
dir_tab.clear(); dir_tab.clear();
last_item = &item; last_item = &item;
is_item = false; is_item = false;
function = 0; // !! dodac jakas empty funkcje function = 0;
param_tab.clear(); param_tab.clear();
anchor.clear(); anchor.clear();
@ -128,43 +130,6 @@ void Request::RequestStarts()
// value can be null
void Request::SetCookie(const char * name, const char * value, PT::Date * expires)
{
headers << "Set-Cookie: " << name << "=";
if( value && value[0]!=0 )
headers << value;
else
headers << "\"\"";
if( expires )
headers << "; expires=" << DateToStrCookie(*expires) << " GMT";
headers << "; path=/; domain=" << config->base_url << "\r\n";
/*
don't use '; secure' flag if you are using both sites (with SSL
and without SSL) -- with secure flag the cookie is sent only through
SSL and if you accidentally open a new window without SSL (http://)
then winix will create a new session for you and the previous session (https://)
will be lost (cookie is overwritten on the client browser)
*/
}
void Request::SetCookie(const char * name, long value, PT::Date * expires)
{
headers << "Set-Cookie: " << name << "=" << value;
if( expires )
headers << "; expires=" << DateToStrCookie(*expires) << " GMT";
headers << "; path=/; domain=" << config->base_url << "\r\n";
}
bool Request::IsPostVar(const wchar_t * var) bool Request::IsPostVar(const wchar_t * var)
{ {

View File

@ -22,6 +22,7 @@
#include "date/date.h" #include "date/date.h"
#include "space/space.h" #include "space/space.h"
#include "space/spacetojson.h" #include "space/spacetojson.h"
#include "textstream/textstream.h"
@ -32,23 +33,164 @@ class FunctionBase;
struct Request struct Request
{ {
// request id /*
// is incremented for each request and is never 0 request id
// (from -1 will be incremented twice) is incremented for each request and is never 0
// it's used for some optimalizations e.g. in templates (from -1 will be incremented to one)
it's used for some optimizations e.g. in templates
*/
size_t id; size_t id;
// !! moze pozbyc sie tego none?
enum Method { get, post, head, none } method;
enum Role { responder, authorizer } role;
// headers, page and debug /*
//std::ostringstream headers, page, debug; request start time
// !! IMPROVE ME change headers to some kind of a map, may PT::Space ? Time() methods are very slow so it is better to directly use those two values
TextStream<std::string> headers; they are set when a request starts
HtmlTextStream debug; */
time_t start_time;
PT::Date start_date;
/*
*
*
*
* variables representing input from client's browser
*
*
*
*/
/*
the HTTP method
!! IMPROVE ME add the rest methods here
*/
enum Method { get, post, head, unknown_method } method;
/*
subdomain
subdomain = HTTP_HOST environment variable - config->base_url
*/
std::wstring subdomain;
/*
raw parameters
!! CHECK ME may post_tab and cookie_tab should be changed to PT::Space now?
or may change the name to cookie_in? or in_cookie?
*/
PostTab post_tab;
PostFileTab post_file_tab;
CookieTab cookie_tab;
/*
html anchor (those part of URI after '#' character)
*/
std::wstring anchor;
// environment variables
// they are not null -- when the server doesn't have such a variable
// it will be pointing into 'char_empty' which is default '\0'
// !! IMPROVE ME change it to std::wstring, or may PT::Space too?
const char * env_request_method;
const char * env_request_uri;
const char * env_http_cookie;
const char * env_remote_addr;
const char * env_http_host;
const char * env_http_user_agent;
const char * env_http_accept_encoding;
const char * env_fcgi_role;
const char * env_content_type;
const char * env_https;
// current IP address of the remote host (read from REMOTE_ADDR environment variable)
// (at the moment only IPv4 are supported)
int ip;
// true if the browser is Microsoft Internet Explorer
bool browser_msie;
// true if the browser is Konqueror
bool browser_konqueror;
// true if we are using an encrypted connection (SSL)
bool using_ssl;
/*
request input variables representing the winix filesystem
*/
// current directory
std::vector<Item*> dir_tab;
// true if a file exists
bool is_item;
// current file (valid if is_item is true)
Item item;
// current winix function
// null if there is no a function
FunctionBase * function;
// parameters (name:value)
// !! CHECK ME may it should be changed to PT::Space?
ParamTab param_tab;
// this is a pointer either to the item (if exists) or to the last directory
Item * last_item;
/*
*
*
*
* variables for generating output to the client's browser
*
*
*
*/
// request status
// !! CHANGE ME it'll be better to use ordinary http result codes
Error status;
// if not empty means an address for redirecting to
// it should be url-encoded
std::wstring redirect_to;
// a redirect type
// following redirect types are supported:
// 300 Multiple Choices
// 301 Moved Permanently
// 302 Found
// 303 See Other (default)
// 307 Temporary Redirect
int redirect_type;
// send header X-LIGHTTPD-send-file with path to a file
std::wstring x_sendfile;
// send as attachment (causes generating header: content-disposition: attachment)
bool send_as_attachment;
// headers send to the client (without cookies)
PT::Space out_headers;
// cookies send to the client
// a value can be either a cookie value or the whole cookie string (with domain, date etc)
PT::Space out_cookies;
// winix can return either a text answer or a binary answer // winix can return either a text answer or a binary answer
// if send_bin_stream is true then the binary answer is sent (out_bin_stream) // if send_bin_stream is true then the binary answer is sent (out_bin_stream)
// or if send_bin_stream is false then the text answer is sent // or if send_bin_stream is false then the text answer is sent
@ -147,100 +289,7 @@ struct Request
// default: true // default: true
bool use_html_filter; bool use_html_filter;
// raw parameters
PostTab post_tab;
PostFileTab post_file_tab;
CookieTab cookie_tab;
// html anchor (those part of URI after '#' character)
std::wstring anchor;
// environment variables
// they are not null -- when the server doesn't have such a variable
// it will be pointing into 'char_empty' which is default '\0'
const char * env_request_method;
const char * env_request_uri;
const char * env_http_cookie;
const char * env_remote_addr;
const char * env_http_host;
const char * env_http_user_agent;
const char * env_http_accept_encoding;
const char * env_fcgi_role;
const char * env_content_type;
const char * env_https;
// current IP address of the remote host (read from REMOTE_ADDR environment variable)
// (at the moment only IPv4 are supported)
int ip;
// true if the browser is Microsoft Internet Explorer
bool browser_msie;
// true if the browser is Konqueror
bool browser_konqueror;
// true if we are using encrypted connection (SSL)
bool using_ssl;
// current directory
std::vector<Item*> dir_tab;
// true if a file exists
bool is_item;
// current file (if exists)
Item item;
// current winix function
// null if there is no a function
FunctionBase * function;
// parameters (name:value)
ParamTab param_tab;
// request status
Error status;
// usually items in the current directory (depends on the function)
std::vector<Item> item_tab;
// if not empty means an address for redirecting to
// it should be url-encoded
std::wstring redirect_to;
std::string aredirect_to;
// a redirect type
// following redirect types are supported:
// 300 Multiple Choices
// 301 Moved Permanently
// 302 Found
// 303 See Other (default)
// 307 Temporary Redirect
int redirect_type;
// send header X-LIGHTTPD-send-file with path to a file
std::wstring x_sendfile;
// send as attachment (causes header: content-disposition: attachment)
bool send_as_attachment;
// this is a pointer either to the item (if exists) or to the last directory
Item * last_item;
// request start time
// Time() methods are very slow so it is better to directly use those two values
// they are set when a request starts
time_t start_time;
PT::Date start_date;
// a subdomain
// subdomain = HTTP_HOST environment variable - config->base_url
std::wstring subdomain;
// if this variable is true then winix always return 200 OK header // if this variable is true then winix always return 200 OK header
// when the status would be 404 (not found) or 403 (permission denied) // when the status would be 404 (not found) or 403 (permission denied)
// default: false // default: false
@ -252,6 +301,24 @@ struct Request
bool gen_use_special_chars; bool gen_use_special_chars;
/*
additional variables used for common uses
*/
// usually items in the current directory (depends on the function)
std::vector<Item> item_tab;
Request(); Request();
void SetConfig(Config * pconfig); void SetConfig(Config * pconfig);
void RequestStarts(); void RequestStarts();
@ -261,29 +328,32 @@ struct Request
bool IsParam(const wchar_t * param_name); bool IsParam(const wchar_t * param_name);
bool IsParam(const std::wstring & param_name); bool IsParam(const std::wstring & param_name);
const std::wstring & ParamValue(const wchar_t * param_name); // returns an empty string if there is no such a parameter const std::wstring & ParamValue(const wchar_t * param_name); // returns an empty string if there is no such a parameter
const std::wstring & ParamValue(const std::wstring & param_name); // returns an empty string if there is no such a parameter const std::wstring & ParamValue(const std::wstring & param_name); // returns an empty string if there is no such a parameter
void SetCookie(const char * name, const char * value, PT::Date * expires = 0);
void SetCookie(const char * name, long value, PT::Date * expires = 0);
bool IsPostVar(const wchar_t * var); bool IsPostVar(const wchar_t * var);
bool IsPostVar(const std::wstring & var); bool IsPostVar(const std::wstring & var);
const std::wstring & PostVar(const wchar_t * var); // returns an empty string if there is no such a parameter const std::wstring & PostVar(const wchar_t * var); // returns an empty string if there is no such a parameter
const std::wstring & PostVar(const std::wstring & var); // returns an empty string if there is no such a parameter const std::wstring & PostVar(const std::wstring & var); // returns an empty string if there is no such a parameter
bool PostVar(const wchar_t * var, std::wstring & result); bool PostVar(const wchar_t * var, std::wstring & result);
bool PostVar(const std::wstring & var, std::wstring & result); bool PostVar(const std::wstring & var, std::wstring & result);
std::wstring * PostVarp(const wchar_t * var); std::wstring * PostVarp(const wchar_t * var);
std::wstring * PostVarp(const std::wstring & var); std::wstring * PostVarp(const std::wstring & var);
bool AllPostVarEmpty(); // returning true if all post vars are empty bool AllPostVarEmpty(); // returning true if all post vars are empty
void SendAll();
// setting a cookie
// name - cookie name (either const wchar_t, or std::wstring or PT::WTextStream)
// value - cookie value (can be everything which can be put to PT::WTextStream stream)
// the return std::wstring reference is a reference to the cookie inserted value (in out_cookies structure)
template<typename NameType, typename ValueType>
std::wstring & AddCookie(const NameType & name, const ValueType & value, PT::Date * expires = 0);
template<typename NameType, typename ValueType>
std::wstring & AddCookie(const NameType & name, const ValueType & value, PT::Date & expires);
private: private:
@ -303,8 +373,42 @@ private:
template<typename NameType, typename ValueType>
std::wstring & Request::AddCookie(const NameType & name, const ValueType & value, PT::Date * expires)
{
PT::WTextStream cookie;
cookie << value;
if( cookie.empty() )
cookie << L"\"\""; // cookie empty value
if( expires )
cookie << L"; expires=" << DateToStrCookie(*expires) << L" GMT";
cookie << L"; path=/; domain=" << config->base_url;
/*
!! IMPROVE ME add an option to the config
don't use '; secure' flag if you are using both sites (with SSL
and without SSL) -- with secure flag the cookie is sent only through
SSL and if you accidentally open a new window without SSL (http://)
then winix will create a new session for you and the previous session (https://)
will be lost (the session cookie will be overwritten in the client's browser)
*/
return out_cookies.Add(name, cookie);
}
template<typename NameType, typename ValueType>
std::wstring & Request::AddCookie(const NameType & name, const ValueType & value, PT::Date & expires)
{
return AddCookie(name, value, &expires);
}
#endif #endif

View File

@ -2,7 +2,7 @@
* This file is a part of Winix * This file is a part of Winix
* and is not publicly distributed * and is not publicly distributed
* *
* Copyright (c) 2008-2011, Tomasz Sowa * Copyright (c) 2008-2013, Tomasz Sowa
* All rights reserved. * All rights reserved.
* *
*/ */
@ -16,6 +16,7 @@
#include "textstream/textstream.h" #include "textstream/textstream.h"
// !! IMPROVE ME
// !! narazie uzywane tylko w post multi parserze // !! narazie uzywane tylko w post multi parserze
// dodac do zwyklego parsera post // dodac do zwyklego parsera post
#define WINIX_POSTTABLE_MAXSIZE 50 #define WINIX_POSTTABLE_MAXSIZE 50
@ -43,7 +44,7 @@ typedef std::map<std::wstring, std::wstring> PostTab;
typedef std::map<std::wstring, PostFile> PostFileTab; typedef std::map<std::wstring, PostFile> PostFileTab;
typedef std::vector<Param> ParamTab; typedef std::vector<Param> ParamTab;
typedef std::map<std::string, std::string> CookieTab; typedef std::map<std::wstring, std::wstring> CookieTab;
typedef PT::TextStreamBase<char, 1, 4096> BinaryPage; typedef PT::TextStreamBase<char, 1, 4096> BinaryPage;

View File

@ -157,7 +157,7 @@ void SessionManager::SetTemporarySession()
bool SessionManager::SetSessionFromCookie(const std::string & cookie) bool SessionManager::SetSessionFromCookie(const std::wstring & cookie)
{ {
long id = Tol(cookie.c_str()); long id = Tol(cookie.c_str());
SessionContainer::Iterator s = session_tab.FindById(id); SessionContainer::Iterator s = session_tab.FindById(id);

View File

@ -88,7 +88,7 @@ private:
long CreateSessionId(); long CreateSessionId();
void CreateSession(); void CreateSession();
bool SetSessionFromCookie(const std::string & cookie); bool SetSessionFromCookie(const std::wstring & cookie);
void SetTemporarySession(); void SetTemporarySession();
// second thread // second thread