remove Request::post_tab and add Request::http_status
Instead of Reqest::post_tab we use now Request::post_in (pt::Space). Request::http_status will be used instead Request::status, but at the moment is not changed in all places. Request::status has been marked as depracated. While here: - Check for root dir in App and not in FunctionParser, let FunctionParser only log the root dir. - Read post variables after parsing url parameters, this allows winix functions to set limits for post input. - Set limits when parsing input json format, new options added to config: post_max_object_items, post_max_table_items, post_max_all_items, post_max_nested_objects. There are similar options in each winix function (they are in FunctionBase). - Some refactoring in App. - Add config option: log_whole_http_post if true then the whole parsed post input is logged. - Add config option: post_json_max - max length of input stream for parsing json. - Add config option: templates_request_status, default request_status.html this is an ezc template used as [content] when the request status is not 200_ok. - Fix: Sort winix function didn't show items to sort (fix and do some refactoring as well) - Properly sort items in: ImgCrop, Ls, Sort, Upload - Remove ezc templates: err_404.html, err_per_denied.html - now request_status.html is used.
This commit is contained in:
@@ -612,6 +612,7 @@ bool App::CheckAccessFromPlugins()
|
||||
if( res.res_false > 0 )
|
||||
{
|
||||
cur.request->status = WINIX_ERR_PERMISSION_DENIED;
|
||||
cur.request->http_status = Header::status_403_forbidden;
|
||||
log << log2 << "App: access prevented by a plugin" << logend;
|
||||
return false;
|
||||
}
|
||||
@@ -621,102 +622,155 @@ return true;
|
||||
|
||||
|
||||
|
||||
void App::ProcessRequestThrow()
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* REFACTOR ME
|
||||
*/
|
||||
void App::MakeRenameMeToABetterName()
|
||||
{
|
||||
ReadRequest();
|
||||
|
||||
// when BaseUrlRedirect() return true we didn't have to set everything in cur.request->Read()
|
||||
// in the future cur.request->Read() can be split and at the beginning only environment variables will be read
|
||||
// and then BaseUrlRedirect() will be called (for performance)
|
||||
if( !BaseUrlRedirect() )
|
||||
if( cur.request->function )
|
||||
{
|
||||
if( cur.request->env_request_uri.size() <= WINIX_URL_MAX_SIZE )
|
||||
{
|
||||
functions.Parse(); // parsing directories, files, functions and parameters
|
||||
|
||||
if( cur.request->function )
|
||||
{
|
||||
cur.request->function->fun.set_connector(model_connector); // IMPROVEME may would be better to add set_connector() method to functions?
|
||||
cur.request->function->fun.propagate_connector();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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();
|
||||
|
||||
cur.session = session_manager.PrepareSession();
|
||||
model_connector.set_winix_session(cur.session);
|
||||
|
||||
functions.CheckFunctionAndSymlink(); // here a function can be changed
|
||||
|
||||
if( cur.request->function )
|
||||
{
|
||||
cur.request->function->fun.set_connector(model_connector);
|
||||
cur.request->function->fun.propagate_connector();
|
||||
}
|
||||
|
||||
cur.session = session_manager.CheckIfFunctionRequireSession();
|
||||
model_connector.set_winix_session(cur.session);
|
||||
|
||||
SetLocale();
|
||||
|
||||
if( cur.session->new_session )
|
||||
{
|
||||
cur.session->plugin_data.Resize(plugin.Size());
|
||||
plugin.Call(WINIX_SESSION_CREATED);
|
||||
}
|
||||
|
||||
plugin.Call(WINIX_SESSION_CHANGED);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* IMPROVE ME
|
||||
* it will not have the root directory set
|
||||
* so as a response only a blank page is shown
|
||||
* (root directory is set in funcions.Parse())
|
||||
*
|
||||
* IMPROVE ME
|
||||
* we can add a better return code (http status):
|
||||
* http://www.ietf.org/rfc/rfc2616.txt
|
||||
* "A server SHOULD return 414 (Request-URI Too Long) status if a URI is longer than the server can handle"
|
||||
*
|
||||
*/
|
||||
cur.request->status = WINIX_ERR_PERMISSION_DENIED;
|
||||
log << log1 << "App: the URL is too long: " << cur.request->env_request_uri.size() << logend;
|
||||
}
|
||||
|
||||
if( cur.request->dir_tab.empty() )
|
||||
{
|
||||
log << log1 << "App: there is no a root dir (dir_tab is empty), adding a root dir" << logend;
|
||||
Item * root_dir = system.dirs.GetRootDir();
|
||||
|
||||
if( root_dir )
|
||||
{
|
||||
cur.request->dir_tab.push_back(root_dir);
|
||||
cur.request->last_item = cur.request->dir_tab.back();
|
||||
cur.mount = system.mounts.CalcCurMount();
|
||||
}
|
||||
else
|
||||
{
|
||||
log << log1 << "App: oops, we do not have a root dir" << logend;
|
||||
}
|
||||
}
|
||||
|
||||
if( cur.mount->type != system.mounts.MountTypeStatic() )
|
||||
Make();
|
||||
cur.request->function->fun.set_connector(model_connector); // IMPROVEME may would be better to add set_connector() method to functions?
|
||||
cur.request->function->fun.propagate_connector();
|
||||
}
|
||||
|
||||
SendAnswer();
|
||||
/*
|
||||
* 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);
|
||||
|
||||
cur.mount = system.mounts.CalcCurMount();
|
||||
|
||||
cur.session = session_manager.PrepareSession();
|
||||
model_connector.set_winix_session(cur.session);
|
||||
|
||||
functions.CheckFunctionAndSymlink(); // here a function can be changed
|
||||
|
||||
if( cur.request->function )
|
||||
{
|
||||
cur.request->function->fun.set_connector(model_connector);
|
||||
cur.request->function->fun.propagate_connector();
|
||||
}
|
||||
|
||||
cur.session = session_manager.CheckIfFunctionRequireSession();
|
||||
model_connector.set_winix_session(cur.session);
|
||||
|
||||
SetLocale();
|
||||
|
||||
if( cur.session->new_session )
|
||||
{
|
||||
cur.session->plugin_data.Resize(plugin.Size());
|
||||
plugin.Call(WINIX_SESSION_CREATED);
|
||||
}
|
||||
|
||||
plugin.Call(WINIX_SESSION_CHANGED);
|
||||
|
||||
|
||||
////////////////////////
|
||||
|
||||
|
||||
cur.request->PrepareAnswerType();
|
||||
|
||||
if( cur.session->ip_ban && cur.session->ip_ban->IsIPBanned() )
|
||||
{
|
||||
pt::Date date(cur.session->ip_ban->expires);
|
||||
|
||||
// IMPROVE ME there is no slog now
|
||||
//slog << logerror << T("this_ip_is_banned_until") << ' ' << date << " UTC" << logend;
|
||||
|
||||
cur.request->status = WINIX_ERR_PERMISSION_DENIED;
|
||||
cur.request->http_status = Header::status_403_forbidden;
|
||||
}
|
||||
|
||||
// cur.request->status can be changed by function_parser
|
||||
if( cur.request->status == WINIX_ERR_OK && cur.request->http_status == Header::status_200_ok )
|
||||
plugin.Call(WINIX_PREPARE_REQUEST);
|
||||
|
||||
// if( cur.request->status == WINIX_ERR_OK )
|
||||
// functions.CheckFunctionAndSymlink();
|
||||
|
||||
CheckAccessFromPlugins();
|
||||
|
||||
// !! CHECK ME CheckFunctionAndSymlink can set redirect_to
|
||||
// may it should be tested before calling CheckIfNeedSSLredirect?
|
||||
CheckIfNeedSSLredirect();
|
||||
|
||||
if( !cur.request->redirect_to.empty() )
|
||||
return;
|
||||
|
||||
AddDefaultModels();
|
||||
|
||||
if( cur.request->status == WINIX_ERR_OK && cur.request->http_status == Header::status_200_ok )
|
||||
functions.MakeFunction();
|
||||
|
||||
if( cur.session->spam_score > 0 )
|
||||
log << log1 << "App: spam score: " << cur.session->spam_score << logend;
|
||||
|
||||
if( cur.request->IsParam(L"noredirect") )
|
||||
cur.request->redirect_to.clear();
|
||||
|
||||
if( cur.request->status == WINIX_ERR_OK && cur.request->http_status == Header::status_200_ok )
|
||||
plugin.Call(WINIX_PROCESS_REQUEST);
|
||||
|
||||
CheckPostRedirect();
|
||||
|
||||
if( !cur.request->redirect_to.empty() )
|
||||
return;
|
||||
|
||||
if( cur.request->dir_tab.empty() )
|
||||
{
|
||||
log << log1 << "App: there is no a root dir (dir_tab is empty -- after calling a function)" << logend;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool App::AddRootDir()
|
||||
{
|
||||
Item * root_dir = system.dirs.GetRootDir();
|
||||
|
||||
if( root_dir )
|
||||
{
|
||||
cur.request->dir_tab.push_back(root_dir);
|
||||
cur.request->last_item = cur.request->dir_tab.back();
|
||||
}
|
||||
else
|
||||
{
|
||||
log << log3 << "App: there is no a root directory" << logend;
|
||||
cur.request->http_status = Header::status_404_not_found;
|
||||
}
|
||||
|
||||
return root_dir != nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void App::ProcessRequestThrow()
|
||||
{
|
||||
if( AddRootDir() )
|
||||
{
|
||||
ReadRequest();
|
||||
|
||||
if( !BaseUrlRedirect() )
|
||||
{
|
||||
if( functions.Parse() )
|
||||
{
|
||||
ReadPostVars();
|
||||
}
|
||||
|
||||
if( cur.mount->type != system.mounts.MountTypeStatic() )
|
||||
{
|
||||
MakeRenameMeToABetterName();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -732,6 +786,8 @@ void App::ProcessRequest()
|
||||
log << log2 << config.log_delimiter << logend;
|
||||
|
||||
ProcessRequestThrow();
|
||||
ModifyStatusCodeIfNeeded();
|
||||
SendAnswer();
|
||||
SaveSessionsIfNeeded();
|
||||
|
||||
cur.request->RequestEnds();
|
||||
@@ -784,6 +840,7 @@ void App::ClearAfterRequest()
|
||||
aheader_value.clear();
|
||||
cur.mount = system.mounts.GetEmptyMount();
|
||||
system.mounts.pmount = cur.mount; // IMPROVE ME system.mounts.pmount will be removed
|
||||
post_log_tmp_buffer.clear();
|
||||
// send_data_buf doesn't have to be cleared and it is better to not clear it (optimizing)
|
||||
|
||||
model_connector.set_winix_request(nullptr);
|
||||
@@ -908,70 +965,6 @@ void App::AddDefaultModels()
|
||||
|
||||
|
||||
|
||||
// !! IMPROVE ME change to a better name
|
||||
// may ProcessRequest()? but probably it is already defined...
|
||||
// this method needs some refactoring
|
||||
void App::Make()
|
||||
{
|
||||
if( cur.request->dir_tab.empty() )
|
||||
{
|
||||
log << log1 << "App: there is no a root dir (dir_tab is empty)" << logend;
|
||||
return;
|
||||
}
|
||||
|
||||
cur.request->PrepareAnswerType();
|
||||
|
||||
if( cur.session->ip_ban && cur.session->ip_ban->IsIPBanned() )
|
||||
{
|
||||
pt::Date date(cur.session->ip_ban->expires);
|
||||
|
||||
// IMPROVE ME there is no slog now
|
||||
//slog << logerror << T("this_ip_is_banned_until") << ' ' << date << " UTC" << logend;
|
||||
|
||||
cur.request->status = WINIX_ERR_PERMISSION_DENIED;
|
||||
}
|
||||
|
||||
// cur.request->status can be changed by function_parser
|
||||
if( cur.request->status == WINIX_ERR_OK )
|
||||
plugin.Call(WINIX_PREPARE_REQUEST);
|
||||
|
||||
// if( cur.request->status == WINIX_ERR_OK )
|
||||
// functions.CheckFunctionAndSymlink();
|
||||
|
||||
CheckAccessFromPlugins();
|
||||
|
||||
// !! CHECK ME CheckFunctionAndSymlink can set redirect_to
|
||||
// may it should be tested before calling CheckIfNeedSSLredirect?
|
||||
CheckIfNeedSSLredirect();
|
||||
|
||||
if( !cur.request->redirect_to.empty() )
|
||||
return;
|
||||
|
||||
AddDefaultModels();
|
||||
|
||||
if( cur.request->status == WINIX_ERR_OK )
|
||||
functions.MakeFunction();
|
||||
|
||||
if( cur.session->spam_score > 0 )
|
||||
log << log1 << "App: spam score: " << cur.session->spam_score << logend;
|
||||
|
||||
if( cur.request->IsParam(L"noredirect") )
|
||||
cur.request->redirect_to.clear();
|
||||
|
||||
if( cur.request->status == WINIX_ERR_OK )
|
||||
plugin.Call(WINIX_PROCESS_REQUEST);
|
||||
|
||||
CheckPostRedirect();
|
||||
|
||||
if( !cur.request->redirect_to.empty() )
|
||||
return;
|
||||
|
||||
if( cur.request->dir_tab.empty() )
|
||||
{
|
||||
log << log1 << "App: there is no a root dir (dir_tab is empty -- after calling a function)" << logend;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void App::LogEnvironmentVariables()
|
||||
@@ -1052,7 +1045,7 @@ void App::ReadRequest()
|
||||
LogAccess();
|
||||
ReadEnvHTTPVariables();
|
||||
|
||||
ReadPostVars();
|
||||
//ReadPostVars();
|
||||
|
||||
if( config.log_env_variables )
|
||||
LogEnvironmentVariables();
|
||||
@@ -1071,6 +1064,7 @@ void App::ReadRequest()
|
||||
|
||||
if( cur.request->using_ssl )
|
||||
log << log3 << "App: connection secure through SSL" << logend;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1293,8 +1287,7 @@ void App::LogAccess()
|
||||
}
|
||||
|
||||
|
||||
|
||||
void App::ReadPostJson()
|
||||
void App::ReadInputPostToBuffer()
|
||||
{
|
||||
char buffer[1024];
|
||||
const int buffer_len = sizeof(buffer) / sizeof(char) - 1;
|
||||
@@ -1303,34 +1296,89 @@ void App::ReadPostJson()
|
||||
post_buffer.clear();
|
||||
post_buffer.reserve(1024 * 1024 * 5); // IMPROVEME add to config?
|
||||
|
||||
cur.request->is_postin_used = true;
|
||||
|
||||
do
|
||||
{
|
||||
// IMPROVE ME
|
||||
// we can read to pt::TextBuffer and make a pt::JSONToSpaceParser::Parse(pt::TextBuffer &) method
|
||||
read_len = FCGX_GetStr(buffer, buffer_len, fcgi_request.in);
|
||||
|
||||
if( read_len > 0 )
|
||||
post_buffer.append(buffer, read_len);
|
||||
post_buffer.write(buffer, read_len);
|
||||
}
|
||||
while( read_len == buffer_len );
|
||||
|
||||
}
|
||||
|
||||
|
||||
void App::ParsePostJson()
|
||||
{
|
||||
FunctionBase * fun = cur.request->function;
|
||||
|
||||
space_parser.set_object_items_limit( (fun && fun->post_max_object_items != 0) ? fun->post_max_object_items : config.post_max_object_items);
|
||||
space_parser.set_table_items_limit( (fun && fun->post_max_table_items != 0) ? fun->post_max_table_items : config.post_max_table_items);
|
||||
space_parser.set_all_items_limit( (fun && fun->post_max_all_items != 0) ? fun->post_max_all_items : config.post_max_all_items);
|
||||
space_parser.set_nested_level_limit( (fun && fun->post_max_nested_objects != 0) ? fun->post_max_nested_objects : config.post_max_nested_objects);
|
||||
|
||||
pt::SpaceParser::Status parse_status = space_parser.parse_json(post_buffer, cur.request->post_in);
|
||||
|
||||
if( parse_status == pt::SpaceParser::ok )
|
||||
{
|
||||
}
|
||||
else
|
||||
if( parse_status == pt::SpaceParser::syntax_error )
|
||||
{
|
||||
log << log1 << "App: cannot parse the input stream as an JSON object"
|
||||
<< ", syntax error in line: " << space_parser.get_last_parsed_line() << ":" << space_parser.get_last_parsed_column() << logend;
|
||||
|
||||
cur.request->post_in.clear();
|
||||
cur.request->http_status = Header::status_400_bad_request;
|
||||
}
|
||||
else
|
||||
if( parse_status == pt::SpaceParser::limit_object_items_exceeded )
|
||||
{
|
||||
log << log1 << "App: object items limit exceeded when parsing input JSON object" << logend;
|
||||
cur.request->post_in.clear();
|
||||
cur.request->http_status = Header::status_400_bad_request;
|
||||
}
|
||||
else
|
||||
if( parse_status == pt::SpaceParser::limit_table_items_exceeded )
|
||||
{
|
||||
log << log1 << "App: table items limit exceeded when parsing input JSON object" << logend;
|
||||
cur.request->post_in.clear();
|
||||
cur.request->http_status = Header::status_400_bad_request;
|
||||
}
|
||||
else
|
||||
if( parse_status == pt::SpaceParser::limit_all_items_exceeded )
|
||||
{
|
||||
log << log1 << "App: all items limit exceeded when parsing input JSON object" << logend;
|
||||
cur.request->post_in.clear();
|
||||
cur.request->http_status = Header::status_400_bad_request;
|
||||
}
|
||||
else
|
||||
if( parse_status == pt::SpaceParser::limit_nested_level_exceeded )
|
||||
{
|
||||
log << log1 << "App: nested objects/tables limit exceeded when parsing input JSON object" << logend;
|
||||
cur.request->post_in.clear();
|
||||
cur.request->http_status = Header::status_400_bad_request;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void App::ReadPostJson()
|
||||
{
|
||||
ReadInputPostToBuffer();
|
||||
|
||||
if( !post_buffer.empty() )
|
||||
{
|
||||
pt::SpaceParser::Status status = space_parser.parse_json(post_buffer.c_str(), cur.request->post_in);
|
||||
post_buffer.clear();
|
||||
|
||||
if( status != pt::SpaceParser::ok )
|
||||
if( config.post_json_max == 0 || post_buffer.size() <= config.post_json_max )
|
||||
{
|
||||
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?)
|
||||
ParsePostJson();
|
||||
}
|
||||
else
|
||||
{
|
||||
log << log1 << "App: the input stream exceeded the limit of " << config.post_json_max << " bytes (skipping parsing)" << logend;
|
||||
cur.request->http_status = Header::status_400_bad_request;
|
||||
}
|
||||
|
||||
post_buffer.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1342,26 +1390,29 @@ void App::ReadPostJson()
|
||||
|
||||
void App::ReadPostVars()
|
||||
{
|
||||
// CHECKME
|
||||
// what about errors during parsing input?
|
||||
|
||||
if( cur.request->method == Request::post || cur.request->method == Request::delete_ )
|
||||
{
|
||||
if( pt::is_substr_nc(L"multipart/form-data", cur.request->env_content_type.c_str()) )
|
||||
{
|
||||
log << log3 << "App: post content type: multipart/form-data" << logend;
|
||||
post_multi_parser.Parse(fcgi_request.in, cur.request->post_tab, cur.request->post_file_tab);
|
||||
post_multi_parser.Parse(fcgi_request.in, *cur.request); // IMPROVEME add checking for return status
|
||||
}
|
||||
else
|
||||
if( pt::is_substr_nc(Winix::Header::application_json, cur.request->env_content_type.c_str()) )
|
||||
{
|
||||
log << log3 << "App: post content type: " << Winix::Header::application_json << logend;
|
||||
log << log3 << "App: post content type: " << Winix::Header::application_json << ", using json parser" << logend;
|
||||
ReadPostJson();
|
||||
}
|
||||
else
|
||||
{
|
||||
// IMPROVE ME may to check a correct content-type header?
|
||||
post_parser.Parse(fcgi_request.in, cur.request->post_tab);
|
||||
post_parser.Parse(fcgi_request.in, *cur.request); // IMPROVEME add checking for return status
|
||||
}
|
||||
|
||||
if( config.log_whole_http_post )
|
||||
{
|
||||
cur.request->post_in.serialize_to_json_stream(post_log_tmp_buffer, true);
|
||||
log << log3 << "App: the whole http post after parsing:" << logend << post_log_tmp_buffer << logend;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1464,7 +1515,7 @@ return false;
|
||||
}
|
||||
|
||||
|
||||
bool App::PrepareHeadersStaticCreateResource(pt::WTextStream & out_path)
|
||||
bool App::CreateStaticResourcePath(pt::WTextStream & out_path)
|
||||
{
|
||||
size_t i = 0;
|
||||
Item * dir = system.dirs.GetDir(system.mounts.pmount->dir_id);
|
||||
@@ -1476,7 +1527,7 @@ bool App::PrepareHeadersStaticCreateResource(pt::WTextStream & out_path)
|
||||
}
|
||||
|
||||
size_t how_many_dirs = system.dirs.DirLevel(dir->id);
|
||||
const wchar_t * path = SkipDirs(cur.request->env_request_uri.c_str(), how_many_dirs);
|
||||
const wchar_t * path = SkipDirs(cur.request->env_request_uri.c_str(), how_many_dirs);
|
||||
|
||||
// the path begins with a slash only if how_many_dirs is zero
|
||||
while( *path == '/' )
|
||||
@@ -1492,12 +1543,12 @@ return true;
|
||||
}
|
||||
|
||||
|
||||
void App::PrepareHeadersStatic()
|
||||
void App::PrepareSendFileHeaderForStaticMountpoint()
|
||||
{
|
||||
if( PathHasUpDir(cur.request->env_request_uri) )
|
||||
{
|
||||
log << log1 << "App: incorrect path for a static file" << logend;
|
||||
PrepareHeadersForbidden();
|
||||
cur.request->http_status = Header::status_403_forbidden;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1507,27 +1558,27 @@ void App::PrepareHeadersStatic()
|
||||
if( index >= config.static_dirs.size() )
|
||||
{
|
||||
log << log1 << "App: static dir with index " << index << " is not defined in the config" << logend;
|
||||
PrepareHeadersForbidden();
|
||||
cur.request->http_status = Header::status_403_forbidden;
|
||||
return;
|
||||
}
|
||||
|
||||
pt::WTextStream path;
|
||||
path << config.static_dirs[index] << L"/";
|
||||
|
||||
if( !PrepareHeadersStaticCreateResource(path) )
|
||||
if( !CreateStaticResourcePath(path) )
|
||||
{
|
||||
PrepareHeadersForbidden();
|
||||
cur.request->http_status = Header::status_403_forbidden;
|
||||
return;
|
||||
}
|
||||
|
||||
AddHeader(L"Status", L"200 OK");
|
||||
|
||||
/*
|
||||
* FIX ME now we can send full path (apache, lighttpd) and relative path (nginx)
|
||||
* but this feature for mounting static content probably will be removed
|
||||
* FIX ME now we can send full path (apache, lighttpd) and relative path (nginx)
|
||||
* but this feature for mounting static content probably will be removed
|
||||
*/
|
||||
if( AddHeader(config.send_file_header, path) )
|
||||
{
|
||||
log << log2 << "App: sending a file from a static mountpoint: " << path << logend;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1579,80 +1630,58 @@ void App::PrepareHeaderContentType()
|
||||
|
||||
|
||||
|
||||
void App::PrepareHeadersForbidden()
|
||||
{
|
||||
AddHeader(L"Status", L"403 Forbidden");
|
||||
PrepareHeaderContentType();
|
||||
}
|
||||
|
||||
|
||||
void App::PrepareHeadersRedirect()
|
||||
void App::ModifyStatusForRedirect()
|
||||
{
|
||||
switch(cur.request->redirect_type)
|
||||
{
|
||||
case 300:
|
||||
AddHeader(L"Status", L"300 Multiple Choices");
|
||||
cur.request->http_status = Header::status_300_multiple_choices;
|
||||
break;
|
||||
|
||||
case 301:
|
||||
AddHeader(L"Status", L"301 Moved Permanently");
|
||||
cur.request->http_status = Header::status_301_moved_permanently;
|
||||
break;
|
||||
|
||||
case 302:
|
||||
AddHeader(L"Status", L"302 Found");
|
||||
cur.request->http_status = Header::status_302_found;
|
||||
break;
|
||||
|
||||
case 307:
|
||||
AddHeader(L"Status", L"307 Temporary Redirect");
|
||||
cur.request->http_status = Header::status_307_temporary_redirect;
|
||||
break;
|
||||
|
||||
case 303:
|
||||
default:
|
||||
AddHeader(L"Status", L"303 See Other");
|
||||
cur.request->http_status = Header::status_303_see_other;
|
||||
break;
|
||||
}
|
||||
|
||||
AddHeader(L"Location", cur.request->redirect_to);
|
||||
log << log2 << "App: redirect to: " << cur.request->redirect_to << logend;
|
||||
}
|
||||
|
||||
|
||||
void App::PrepareHeadersSendFile()
|
||||
void App::PrepareSendFileHeader()
|
||||
{
|
||||
AddHeader(L"Status", L"200 OK");
|
||||
|
||||
if( AddHeader(config.send_file_header, cur.request->x_sendfile) )
|
||||
{
|
||||
log << log2 << "App: sending file: " << cur.request->x_sendfile << logend;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void App::PrepareHeadersCompression(int compress_encoding)
|
||||
void App::PrepareContentEncodingHeader(int compress_encoding)
|
||||
{
|
||||
if( compress_encoding == 0 || compress_encoding == 1 )
|
||||
{
|
||||
AddHeader(L"Content-Encoding", L"deflate");
|
||||
}
|
||||
else
|
||||
{
|
||||
AddHeader(L"Content-Encoding", L"gzip");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void App::PrepareHeadersNormal(Header header, size_t output_size)
|
||||
void App::PrepareContentLengthHeader(size_t output_size)
|
||||
{
|
||||
switch( header )
|
||||
{
|
||||
case h_404:
|
||||
AddHeader(L"Status", L"404 Not Found");
|
||||
PrepareHeaderContentType();
|
||||
break;
|
||||
|
||||
case h_403:
|
||||
PrepareHeadersForbidden();
|
||||
break;
|
||||
|
||||
default:
|
||||
AddHeader(L"Status", L"200 OK");
|
||||
PrepareHeaderContentType();
|
||||
}
|
||||
|
||||
if( output_size != static_cast<size_t>(-1) )
|
||||
{
|
||||
pt::WTextStream buf;
|
||||
@@ -1734,35 +1763,53 @@ void App::SendCookies()
|
||||
}
|
||||
|
||||
|
||||
void App::PrepareHeaders(bool compressing, int compress_encoding, Header header, size_t output_size)
|
||||
void App::PrepareHeaderStatus(int http_status)
|
||||
{
|
||||
pt::WTextStream value;
|
||||
Header::prepare_status_value(http_status, value, false);
|
||||
AddHeader(L"Status", value);
|
||||
log << log2 << "App: http status: " << value << logend;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void App::PrepareHeaders(bool compressing, int compress_encoding, size_t output_size)
|
||||
{
|
||||
PrepareSessionCookie();
|
||||
|
||||
if( cur.request->send_as_attachment )
|
||||
{
|
||||
AddHeader(L"Content-Disposition", L"attachment");
|
||||
}
|
||||
|
||||
//if( !cur.request->redirect_to.empty() && !cur.request->return_json )
|
||||
if( !cur.request->redirect_to.empty() )
|
||||
{
|
||||
PrepareHeadersRedirect();
|
||||
ModifyStatusForRedirect();
|
||||
AddHeader(L"Location", cur.request->redirect_to);
|
||||
log << log2 << "App: redirect to: " << cur.request->redirect_to << logend;
|
||||
}
|
||||
else
|
||||
if( system.mounts.pmount->type == system.mounts.MountTypeStatic() )
|
||||
{
|
||||
PrepareHeadersStatic();
|
||||
PrepareSendFileHeaderForStaticMountpoint();
|
||||
}
|
||||
else
|
||||
if( !cur.request->x_sendfile.empty() )
|
||||
{
|
||||
PrepareHeadersSendFile();
|
||||
PrepareSendFileHeader();
|
||||
}
|
||||
else
|
||||
{
|
||||
PrepareHeadersNormal(header, output_size);
|
||||
PrepareContentLengthHeader(output_size);
|
||||
}
|
||||
|
||||
if( compressing )
|
||||
PrepareHeadersCompression(compress_encoding);
|
||||
{
|
||||
PrepareContentEncodingHeader(compress_encoding);
|
||||
}
|
||||
|
||||
PrepareHeaderStatus(cur.request->http_status);
|
||||
PrepareHeaderContentType();
|
||||
}
|
||||
|
||||
|
||||
@@ -1804,7 +1851,6 @@ void App::SelectCompression(size_t source_len, bool & compression_allowed, int &
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if( config.compression_encoding == 2 || config.compression_encoding == 20 )
|
||||
{
|
||||
if( accept_encoding_parser.AcceptGzip() )
|
||||
@@ -1833,54 +1879,53 @@ bool App::CanSendContent()
|
||||
return false;
|
||||
}
|
||||
|
||||
// if( cur.request->return_json )
|
||||
// {
|
||||
// // if there is a redirect flag then it will be put to info struct
|
||||
// return true;
|
||||
// }
|
||||
|
||||
if( !cur.request->redirect_to.empty() )
|
||||
{
|
||||
// if there is a redirect and no json is requred then we do not send the content
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
we don't have to check the HEAD method
|
||||
the server (lighttpd) doesn't send the body of its own
|
||||
*/
|
||||
if( cur.request->method == Request::head )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
App::Header App::GetHTTPStatusCode()
|
||||
void App::ModifyStatusCodeIfNeeded()
|
||||
{
|
||||
Error status = cur.request->status;
|
||||
Header header = h_200;
|
||||
Error status = cur.request->status;
|
||||
|
||||
if( status == WINIX_ERR_NO_ITEM || status == WINIX_ERR_NO_FUNCTION || status == WINIX_ERR_UNKNOWN_PARAM )
|
||||
// moved from Templates when a pattern was selected
|
||||
switch( status )
|
||||
{
|
||||
header = h_404;
|
||||
log << log2 << "App: http response: 404 Not Found" << logend;
|
||||
case WINIX_ERR_INCORRECT_URI: // !!temporarily
|
||||
case WINIX_ERR_INTERNAL_ERROR: // !! temprarily
|
||||
case WINIX_ERR_PERMISSION_DENIED:
|
||||
case WINIX_ERR_CANT_CHANGE_USER:
|
||||
case WINIX_ERR_CANT_CHANGE_GROUP:
|
||||
case WINIX_ERR_CANT_CHANGE_PRIVILEGES:
|
||||
cur.request->http_status = Header::status_403_forbidden;
|
||||
break;
|
||||
|
||||
case WINIX_ERR_NO_ITEM:
|
||||
case WINIX_ERR_NO_FUNCTION:
|
||||
case WINIX_ERR_UNKNOWN_PARAM:
|
||||
cur.request->http_status = Header::status_404_not_found;
|
||||
break;
|
||||
}
|
||||
|
||||
if( status == WINIX_ERR_PERMISSION_DENIED || status == WINIX_ERR_CANT_CHANGE_USER || status == WINIX_ERR_CANT_CHANGE_GROUP )
|
||||
{
|
||||
header = h_403;
|
||||
log << log2 << "App: http response: 403 Forbidden" << logend;
|
||||
}
|
||||
|
||||
if( cur.request->use_200_status_for_not_found_and_permission_denied && (header == h_404 || header == h_403) )
|
||||
if( cur.request->use_200_status_for_not_found_and_permission_denied && (
|
||||
cur.request->http_status == Header::status_404_not_found ||
|
||||
cur.request->http_status == Header::status_403_forbidden
|
||||
))
|
||||
{
|
||||
cur.request->http_status = Header::status_200_ok;
|
||||
log << log3 << "App: changing the http response to: 200 OK" << logend;
|
||||
header = h_200;
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
|
||||
@@ -2389,7 +2434,6 @@ void App::Send8bitOutput(BinaryPage & output)
|
||||
{
|
||||
bool compressing = false;
|
||||
int compress_encoding = 0;
|
||||
Header header = GetHTTPStatusCode();
|
||||
size_t output_size = 0;
|
||||
|
||||
SelectCompression(output.size(), compressing, compress_encoding);
|
||||
@@ -2409,7 +2453,7 @@ void App::Send8bitOutput(BinaryPage & output)
|
||||
output_size = output.size();
|
||||
}
|
||||
|
||||
PrepareHeaders(compressing, compress_encoding, header, output_size);
|
||||
PrepareHeaders(compressing, compress_encoding, output_size);
|
||||
SendHeaders();
|
||||
SendCookies();
|
||||
FCGX_PutS("\r\n", fcgi_request.out);
|
||||
|
Reference in New Issue
Block a user