|
|
|
@ -5,7 +5,7 @@
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2010-2019, Tomasz Sowa
|
|
|
|
|
* Copyright (c) 2010-2021, Tomasz Sowa
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
@ -504,6 +504,14 @@ void App::ProcessRequestThrow()
|
|
|
|
|
{
|
|
|
|
|
functions.Parse(); // parsing directories, files, functions and parameters
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* set global connector for now
|
|
|
|
|
* in the future each thread will have its own model_connector
|
|
|
|
|
*
|
|
|
|
|
* don't set connector for item_tab - it will be moved out from request
|
|
|
|
|
*/
|
|
|
|
|
cur.request->item.set_connector(model_connector);
|
|
|
|
|
|
|
|
|
|
if( !cur.request->dir_tab.empty() )
|
|
|
|
|
{
|
|
|
|
|
cur.mount = system.mounts.CalcCurMount();
|
|
|
|
@ -625,6 +633,8 @@ void App::ClearAfterRequest()
|
|
|
|
|
system.mounts.pmount = cur.mount; // IMPROVE ME system.mounts.pmount will be removed
|
|
|
|
|
// send_data_buf doesn't have to be cleared and it is better to not clear it (optimizing)
|
|
|
|
|
|
|
|
|
|
cur.request->item.set_connector(nullptr);
|
|
|
|
|
|
|
|
|
|
log << logendrequest;
|
|
|
|
|
}
|
|
|
|
|
catch(...)
|
|
|
|
@ -700,16 +710,7 @@ void App::CreateJSONAnswer()
|
|
|
|
|
json_out_stream << L"}\n,\n\"info\": ";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( req.info_serializer )
|
|
|
|
|
{
|
|
|
|
|
req.info_serializer->Serialize(req.info, json_out_stream, true);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
json_out_stream << L"{}";
|
|
|
|
|
log << log1 << "App: Request::info_serializer not defined" << logend;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.info.serialize_to_json_stream(json_out_stream, false);
|
|
|
|
|
log << log3 << "App: sending JSON answer";
|
|
|
|
|
|
|
|
|
|
if( !req.return_info_only )
|
|
|
|
@ -831,16 +832,19 @@ void App::LogEnvironmentVariables()
|
|
|
|
|
|
|
|
|
|
void App::LogEnvironmentHTTPVariables()
|
|
|
|
|
{
|
|
|
|
|
PT::Space::Table::iterator i = cur.request->headers_in.table.begin();
|
|
|
|
|
|
|
|
|
|
for( ; i != cur.request->headers_in.table.end() ; ++i)
|
|
|
|
|
if( cur.request->headers_in.is_object() )
|
|
|
|
|
{
|
|
|
|
|
log << log1 << "HTTP Env: " << i->first << "=";
|
|
|
|
|
PT::Space::ObjectType::iterator i = cur.request->headers_in.value.value_object.begin();
|
|
|
|
|
|
|
|
|
|
if( i->second.size() == 1 )
|
|
|
|
|
log << i->second[0] << logend;
|
|
|
|
|
else
|
|
|
|
|
log << "(incorrect value table size, should be one but is " << i->second.size() << ")" << logend;
|
|
|
|
|
for( ; i != cur.request->headers_in.value.value_object.end() ; ++i)
|
|
|
|
|
{
|
|
|
|
|
log << log1 << "HTTP Env: " << i->first << "=";
|
|
|
|
|
|
|
|
|
|
if( i->second->is_wstr() )
|
|
|
|
|
log << *i->second->get_wstr() << logend;
|
|
|
|
|
else
|
|
|
|
|
log << "(incorrect value type, expected wstr)" << logend;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -983,11 +987,12 @@ bool App::SaveEnvHTTPVariable(const char * env)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PT::UTF8ToWide(header_name, http_header);
|
|
|
|
|
PT::UTF8ToWide(header_name, http_header_name);
|
|
|
|
|
PT::UTF8ToWide(header_value, http_header_value);
|
|
|
|
|
|
|
|
|
|
std::wstring & inserted_header = cur.request->headers_in.Add(http_header, L"", true);
|
|
|
|
|
PT::UTF8ToWide(header_value, inserted_header);
|
|
|
|
|
http_header.clear();
|
|
|
|
|
cur.request->headers_in.add(http_header_name, http_header_value);
|
|
|
|
|
http_header_name.clear();
|
|
|
|
|
http_header_value.clear();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
@ -999,11 +1004,11 @@ void App::ReadEnvRemoteIP()
|
|
|
|
|
|
|
|
|
|
if( config.check_proxy_ip_header )
|
|
|
|
|
{
|
|
|
|
|
http_header = L"HTTP_";
|
|
|
|
|
http_header += config.proxy_ip_header;
|
|
|
|
|
PT::ToUpper(http_header);
|
|
|
|
|
http_header_name = L"HTTP_";
|
|
|
|
|
http_header_name += config.proxy_ip_header;
|
|
|
|
|
PT::ToUpper(http_header_name);
|
|
|
|
|
|
|
|
|
|
PT::WideToUTF8(http_header, http_header_8bit);
|
|
|
|
|
PT::WideToUTF8(http_header_name, http_header_8bit);
|
|
|
|
|
v = FCGX_GetParam(http_header_8bit.c_str(), fcgi_request.envp);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
@ -1087,7 +1092,7 @@ void App::ReadPostJson()
|
|
|
|
|
const int buffer_len = sizeof(buffer) / sizeof(char) - 1;
|
|
|
|
|
int read_len;
|
|
|
|
|
|
|
|
|
|
post_json_parser.SetSpace(cur.request->post_in);
|
|
|
|
|
space_parser.SetSpace(cur.request->post_in);
|
|
|
|
|
post_buffer.clear();
|
|
|
|
|
post_buffer.reserve(1024 * 1024 * 5); // IMPROVEME add to config?
|
|
|
|
|
|
|
|
|
@ -1106,13 +1111,17 @@ void App::ReadPostJson()
|
|
|
|
|
|
|
|
|
|
if( !post_buffer.empty() )
|
|
|
|
|
{
|
|
|
|
|
PT::JSONToSpaceParser::Status status = post_json_parser.ParseString(post_buffer.c_str());
|
|
|
|
|
PT::SpaceParser::Status status = space_parser.ParseJSON(post_buffer.c_str());
|
|
|
|
|
post_buffer.clear();
|
|
|
|
|
|
|
|
|
|
if( status != PT::JSONToSpaceParser::ok )
|
|
|
|
|
if( status != PT::SpaceParser::ok )
|
|
|
|
|
{
|
|
|
|
|
log << log1 << "App: cannot parse the input stream as a JSON object, status: " << (int)status << logend;
|
|
|
|
|
cur.request->post_in.Clear();
|
|
|
|
|
log << log1 << "App: cannot parse the input stream as an JSON object";
|
|
|
|
|
|
|
|
|
|
if( status == PT::SpaceParser::syntax_error )
|
|
|
|
|
log << ", syntax error in line: " << space_parser.get_last_parsed_line() << logend;
|
|
|
|
|
|
|
|
|
|
cur.request->post_in.clear();
|
|
|
|
|
// return an error (http error of some kind?)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1202,9 +1211,9 @@ void App::PrepareSessionCookie()
|
|
|
|
|
|
|
|
|
|
bool App::AddHeader(const wchar_t * name, const wchar_t * value)
|
|
|
|
|
{
|
|
|
|
|
if( !cur.request->out_headers.GetValue(name) )
|
|
|
|
|
if( !cur.request->out_headers.has_key(name) )
|
|
|
|
|
{
|
|
|
|
|
cur.request->out_headers.Add(name, value);
|
|
|
|
|
cur.request->out_headers.add(name, value);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1214,9 +1223,9 @@ return false;
|
|
|
|
|
|
|
|
|
|
bool App::AddHeader(const std::wstring & name, const std::wstring & value)
|
|
|
|
|
{
|
|
|
|
|
if( !cur.request->out_headers.GetValue(name) )
|
|
|
|
|
if( !cur.request->out_headers.has_key(name) )
|
|
|
|
|
{
|
|
|
|
|
cur.request->out_headers.Add(name, value);
|
|
|
|
|
cur.request->out_headers.add(name, value);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1226,9 +1235,9 @@ return false;
|
|
|
|
|
|
|
|
|
|
bool App::AddHeader(const wchar_t * name, const PT::WTextStream & value)
|
|
|
|
|
{
|
|
|
|
|
if( !cur.request->out_headers.GetValue(name) )
|
|
|
|
|
if( !cur.request->out_headers.has_key(name) )
|
|
|
|
|
{
|
|
|
|
|
cur.request->out_headers.Add(name, value);
|
|
|
|
|
cur.request->out_headers.add_stream(name, value);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1238,9 +1247,9 @@ return false;
|
|
|
|
|
|
|
|
|
|
bool App::AddHeader(const std::wstring & name, const PT::WTextStream & value)
|
|
|
|
|
{
|
|
|
|
|
if( !cur.request->out_headers.GetValue(name) )
|
|
|
|
|
if( !cur.request->out_headers.has_key(name) )
|
|
|
|
|
{
|
|
|
|
|
cur.request->out_headers.Add(name, value);
|
|
|
|
|
cur.request->out_headers.add_stream(name, value);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1313,34 +1322,29 @@ void App::PrepareHeadersStatic()
|
|
|
|
|
|
|
|
|
|
void App::PrepareHeaderContentType()
|
|
|
|
|
{
|
|
|
|
|
std::wstring * value = 0;
|
|
|
|
|
|
|
|
|
|
if( !cur.request->out_headers.GetValue(L"Content-Type") )
|
|
|
|
|
if( !cur.request->out_headers.has_key(L"Content-Type") )
|
|
|
|
|
{
|
|
|
|
|
if( cur.request->return_json )
|
|
|
|
|
{
|
|
|
|
|
value = &cur.request->out_headers.Add(L"Content-Type", L"application/json");
|
|
|
|
|
cur.request->out_headers.add(L"Content-Type", L"application/json; charset=UTF-8");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
switch( config.content_type_header )
|
|
|
|
|
{
|
|
|
|
|
case 1:
|
|
|
|
|
value = &cur.request->out_headers.Add(L"Content-Type", L"application/xhtml+xml");
|
|
|
|
|
cur.request->out_headers.add(L"Content-Type", L"application/xhtml+xml; charset=UTF-8");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
|
value = &cur.request->out_headers.Add(L"Content-Type", L"application/xml");
|
|
|
|
|
cur.request->out_headers.add(L"Content-Type", L"application/xml; charset=UTF-8");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0:
|
|
|
|
|
default:
|
|
|
|
|
value = &cur.request->out_headers.Add(L"Content-Type", L"text/html");
|
|
|
|
|
cur.request->out_headers.add(L"Content-Type", L"text/html; charset=UTF-8");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( value )
|
|
|
|
|
*value += L"; charset=UTF-8";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1435,25 +1439,32 @@ void App::PrepareHeadersNormal(Header header, size_t output_size)
|
|
|
|
|
// and if compression is enabled the client's browser will not be able to decompress the stream
|
|
|
|
|
void App::SendHeaders()
|
|
|
|
|
{
|
|
|
|
|
PT::Space::Table::iterator i;
|
|
|
|
|
PT::Space::ObjectType::iterator i;
|
|
|
|
|
PT::Space & headers = cur.request->out_headers;
|
|
|
|
|
|
|
|
|
|
plugin.Call(WINIX_PREPARE_TO_SEND_HTTP_HEADERS, &headers);
|
|
|
|
|
|
|
|
|
|
for(i=headers.table.begin() ; i != headers.table.end() ; ++i)
|
|
|
|
|
if( headers.is_object() )
|
|
|
|
|
{
|
|
|
|
|
if( i->second.size() == 1 )
|
|
|
|
|
plugin.Call(WINIX_PREPARE_TO_SEND_HTTP_HEADERS, &headers);
|
|
|
|
|
|
|
|
|
|
for(i=headers.value.value_object.begin() ; i != headers.value.value_object.end() ; ++i)
|
|
|
|
|
{
|
|
|
|
|
PT::WideToUTF8(i->first, aheader_name);
|
|
|
|
|
PT::WideToUTF8(i->second[0], aheader_value);
|
|
|
|
|
if( i->second->is_wstr() )
|
|
|
|
|
{
|
|
|
|
|
PT::WideToUTF8(i->first, aheader_name);
|
|
|
|
|
PT::WideToUTF8(*i->second->get_wstr(), 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);
|
|
|
|
|
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 << log1 << "HTTP Header: " << aheader_name << ": " << aheader_value << logend;
|
|
|
|
|
if( config.log_http_answer_headers )
|
|
|
|
|
log << log1 << "HTTP Header: " << aheader_name << ": " << aheader_value << logend;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
log << log2 << "Skipping HTTP Header: " << i->first << " - it's not a wstr" << logend;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1462,26 +1473,33 @@ void App::SendHeaders()
|
|
|
|
|
|
|
|
|
|
void App::SendCookies()
|
|
|
|
|
{
|
|
|
|
|
PT::Space::Table::iterator i;
|
|
|
|
|
PT::Space::ObjectType::iterator i;
|
|
|
|
|
PT::Space & cookies = cur.request->out_cookies;
|
|
|
|
|
|
|
|
|
|
plugin.Call(WINIX_PREPARE_TO_SEND_HTTP_COOKIES, &cookies);
|
|
|
|
|
|
|
|
|
|
for(i=cookies.table.begin() ; i != cookies.table.end() ; ++i)
|
|
|
|
|
if( cookies.is_object() )
|
|
|
|
|
{
|
|
|
|
|
if( i->second.size() == 1 )
|
|
|
|
|
plugin.Call(WINIX_PREPARE_TO_SEND_HTTP_COOKIES, &cookies);
|
|
|
|
|
|
|
|
|
|
for(i=cookies.value.value_object.begin() ; i != cookies.value.value_object.end() ; ++i)
|
|
|
|
|
{
|
|
|
|
|
PT::WideToUTF8(i->first, aheader_name);
|
|
|
|
|
PT::WideToUTF8(i->second[0], aheader_value);
|
|
|
|
|
if( i->second->is_wstr() )
|
|
|
|
|
{
|
|
|
|
|
PT::WideToUTF8(i->first, aheader_name);
|
|
|
|
|
PT::WideToUTF8(*i->second->get_wstr(), 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);
|
|
|
|
|
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 << log1 << "HTTP Header: Set-Cookie: " << aheader_name << "=" << aheader_value << logend;
|
|
|
|
|
if( config.log_http_answer_headers )
|
|
|
|
|
log << log1 << "HTTP Header: Set-Cookie: " << aheader_name << "=" << aheader_value << logend;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
log << log2 << "Skipping Cookie: " << i->first << " - it's not a wstr" << logend;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1522,14 +1540,14 @@ void App::PrepareStandardJSONFields()
|
|
|
|
|
{
|
|
|
|
|
PT::Space & info = cur.request->info;
|
|
|
|
|
|
|
|
|
|
if( !info.GetFirstValue(L"status") )
|
|
|
|
|
if( !info.has_key(L"status") )
|
|
|
|
|
{
|
|
|
|
|
info.Add(L"status", cur.request->status);
|
|
|
|
|
info.add(L"status", cur.request->status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( !cur.request->redirect_to.empty() && !info.GetFirstValue(L"redirect_to") )
|
|
|
|
|
if( !cur.request->redirect_to.empty() && !info.has_key(L"redirect_to") )
|
|
|
|
|
{
|
|
|
|
|
info.Add(L"redirect_to", cur.request->redirect_to);
|
|
|
|
|
info.add(L"redirect_to", cur.request->redirect_to);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1824,12 +1842,6 @@ int compress_encoding;
|
|
|
|
|
|
|
|
|
|
void App::SendAnswer()
|
|
|
|
|
{
|
|
|
|
|
if( !cur.request->info_serializer )
|
|
|
|
|
{
|
|
|
|
|
json_generic_serializer.Clear(); // !! IMPROVE ME add to the end of a request
|
|
|
|
|
cur.request->info_serializer = &json_generic_serializer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( cur.request->return_json )
|
|
|
|
|
PrepareStandardJSONFields();
|
|
|
|
|
|
|
|
|
|