From ca14b1a4270e3b30cde80965cecf937d9d6e6dc9 Mon Sep 17 00:00:00 2001 From: Tomasz Sowa Date: Wed, 25 Apr 2018 19:48:47 +0000 Subject: [PATCH] added: Request::header_in (Space) http input headers (without cookies) added: config parameter: log_env_http_variables (bool) (for logging http headers) added: support for DELETE http method, added method FunctionBase::MakeDelete() changed: winix version incremented to 0.6.6 git-svn-id: svn://ttmath.org/publicrep/winix/trunk@1100 e52654a7-88a9-db11-a3e9-0013d4bc506e --- winixd/core/app.cpp | 116 ++++++++++++++++++++++++++++-- winixd/core/app.h | 7 +- winixd/core/config.cpp | 1 + winixd/core/config.h | 3 + winixd/core/misc.h | 10 ++- winixd/core/request.cpp | 2 + winixd/core/request.h | 25 ++++++- winixd/core/version.h | 2 +- winixd/functions/functionbase.cpp | 6 +- winixd/functions/functionbase.h | 3 +- winixd/functions/functions.cpp | 8 ++- 11 files changed, 169 insertions(+), 14 deletions(-) diff --git a/winixd/core/app.cpp b/winixd/core/app.cpp index a88909f..ed395d9 100644 --- a/winixd/core/app.cpp +++ b/winixd/core/app.cpp @@ -711,6 +711,22 @@ 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) + { + log << log1 << "HTTP Env: " << i->first << "="; + + 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; + } +} + + /* * reading the request (without GET parameters in the URL) @@ -724,6 +740,7 @@ void App::ReadRequest() SetSubdomain(); LogAccess(); + ReadEnvHTTPVariables(); ReadPostVars(); @@ -733,6 +750,9 @@ void App::ReadRequest() if( config.log_env_variables ) LogEnvironmentVariables(); + if( config.log_env_http_variables ) + LogEnvironmentHTTPVariables(); + CheckIE(); CheckKonqueror(); @@ -759,17 +779,102 @@ void App::ReadEnvVariables() { SetEnv("REQUEST_METHOD", cur.request->env_request_method); SetEnv("REQUEST_URI", cur.request->env_request_uri); - SetEnv("HTTP_COOKIE", cur.request->env_http_cookie); SetEnv("REMOTE_ADDR", cur.request->env_remote_addr); - SetEnv("HTTP_HOST", cur.request->env_http_host); - SetEnv("HTTP_USER_AGENT", cur.request->env_http_user_agent); SetEnv("FCGI_ROLE", cur.request->env_fcgi_role); SetEnv("CONTENT_TYPE", cur.request->env_content_type); - SetEnv("HTTP_ACCEPT_ENCODING", cur.request->env_http_accept_encoding); SetEnv("HTTPS", cur.request->env_https); + + SetEnv("HTTP_HOST", cur.request->env_http_host); + SetEnv("HTTP_USER_AGENT", cur.request->env_http_user_agent); + SetEnv("HTTP_COOKIE", cur.request->env_http_cookie); + SetEnv("HTTP_ACCEPT_ENCODING", cur.request->env_http_accept_encoding); } +// reading from fastcgi env +void App::ReadEnvHTTPVariables() +{ + const char http_prefix[] = "HTTP_"; + size_t http_prefix_len = sizeof(http_prefix) / sizeof(char) - 1; // 1 means terminating null character + size_t http_headers_saved = 0; + + for(char ** e = fcgi_request.envp ; *e && http_headers_saved < Request::MAX_INPUT_HEADERS ; ++e) + { + char * env = *e; + + if( IsSubStringNoCasep("HTTP_", env) ) + { + env += http_prefix_len; + + // cookies we have in a different table + if( !IsSubStringNoCasep("COOKIE=", env) ) + { + if( SaveEnvHTTPVariable(env) ) + { + http_headers_saved += 1; + } + } + } + } + + if( http_headers_saved == Request::MAX_INPUT_HEADERS ) + { + log << log4 << "App: the maximum number of HTTP headers has been reached, skipping the rest" << logend; + } +} + + +/* + * headers in fcgi are in the form of name=value + */ +bool App::SaveEnvHTTPVariable(const char * env) +{ + // CHECK ME may move to a better place? Request::INPUT_HEADER_VALUE_MAX_LENGTH is a high value + char header_name[Request::INPUT_HEADER_NAME_MAX_LENGTH + 1]; + char header_value[Request::INPUT_HEADER_VALUE_MAX_LENGTH + 1]; + + size_t i = 0; + + for( ; env[i] != 0 && env[i] != '=' && i < Request::INPUT_HEADER_NAME_MAX_LENGTH ; ++i) + { + header_name[i] = ToSmall(env[i]); + } + + header_name[i] = 0; + + if( env[i] != '=' ) + { + // too long header name, skipping + log << log4 << "App: skipped HTTP header \"" << env << "\" because the header name is too long" << logend; + return false; + } + + i += 1; // skipping '=' character + size_t h = 0; + + for( ; env[i] != 0 && h < Request::INPUT_HEADER_VALUE_MAX_LENGTH ; ++i, ++h) + { + header_value[h] = env[i]; + } + + header_value[h] = 0; + + if( env[i] != 0 ) + { + // too long header value, skipping + log << log4 << "App: skipped HTTP header \"" << env << "\" because the header value is too long" << logend; + return false; + } + + PT::UTF8ToWide(header_name, http_header); + + std::wstring & inserted_header = cur.request->headers_in.Add(http_header, L"", true); + PT::UTF8ToWide(header_value, inserted_header); + http_header.clear(); + + return true; +} + void App::ReadEnvRemoteIP() { @@ -798,6 +903,9 @@ void App::CheckRequestMethod() else if( ToSmall(cur.request->env_request_method[0]) == 'h' ) cur.request->method = Request::head; + else + if( ToSmall(cur.request->env_request_method[0]) == 'd' ) + cur.request->method = Request::delete_; } } diff --git a/winixd/core/app.h b/winixd/core/app.h index 804b78d..2121f57 100644 --- a/winixd/core/app.h +++ b/winixd/core/app.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2010-2014, Tomasz Sowa + * Copyright (c) 2010-2018, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -156,6 +156,8 @@ private: std::string output_8bit; BinaryPage compressed_output; std::wstring cookie_id_string; + std::wstring http_header; + bool InitFCGI(char * sock, char * sock_user, char * sock_group); bool InitFCGIChmodChownSocket(char * sock, char * sock_user, char * sock_group); @@ -182,9 +184,12 @@ private: void SendAnswer(); void LogEnvironmentVariables(); + void LogEnvironmentHTTPVariables(); void SetEnv(const char * name, std::wstring & env); void ReadEnvVariables(); + void ReadEnvHTTPVariables(); + bool SaveEnvHTTPVariable(const char * env); void ReadEnvRemoteIP(); void ReadPostVars(); diff --git a/winixd/core/config.cpp b/winixd/core/config.cpp index cfb4a43..202c290 100644 --- a/winixd/core/config.cpp +++ b/winixd/core/config.cpp @@ -147,6 +147,7 @@ void Config::AssignValues(bool stdout_is_closed) log_plugin_call = Bool(L"log_plugin_call", false); log_post_value_size = Size(L"log_post_value_size", 80); log_env_variables = Bool(L"log_env_variables", false); + log_env_http_variables = Bool(L"log_env_http_variables", false); log_http_answer_headers = Bool(L"log_http_answer_headers", false); post_file_max = Size(L"post_file_max", 8388608); // 8 MB diff --git a/winixd/core/config.h b/winixd/core/config.h index ce09a90..4adacc9 100644 --- a/winixd/core/config.h +++ b/winixd/core/config.h @@ -133,6 +133,9 @@ public: // log environment variables (fastcgi environment) bool log_env_variables; + // log environment http variables (only HTTP_* variables from fastcgi environment) + bool log_env_http_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 diff --git a/winixd/core/misc.h b/winixd/core/misc.h index c111474..f4e714b 100644 --- a/winixd/core/misc.h +++ b/winixd/core/misc.h @@ -416,7 +416,7 @@ bool IsSubString(const StringType1 & short_str, const StringType2 & long_str) template -bool IsSubStringNoCase(const StringType1 * short_str, const StringType2 * long_str) +bool IsSubStringNoCasep(const StringType1 * short_str, const StringType2 * long_str) { while( *short_str && *long_str && ToSmall(*short_str) == ToSmall(*long_str) ) { @@ -430,11 +430,17 @@ bool IsSubStringNoCase(const StringType1 * short_str, const StringType2 * long_s return false; } +template +bool IsSubStringNoCase(const StringType1 * short_str, const StringType2 * long_str) +{ + return IsSubStringNoCasep(short_str, long_str); +} + template bool IsSubStringNoCase(const StringType1 & short_str, const StringType2 & long_str) { - return IsSubStringNoCase(short_str.c_str(), long_str.c_str()); + return IsSubStringNoCasep(short_str.c_str(), long_str.c_str()); } diff --git a/winixd/core/request.cpp b/winixd/core/request.cpp index 6914be8..f6896d4 100644 --- a/winixd/core/request.cpp +++ b/winixd/core/request.cpp @@ -96,6 +96,8 @@ void Request::Clear() method = unknown_method; + headers_in.Clear(); + out_headers.Clear(); out_cookies.Clear(); diff --git a/winixd/core/request.h b/winixd/core/request.h index efe4d70..c560b8b 100644 --- a/winixd/core/request.h +++ b/winixd/core/request.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2008-2015, Tomasz Sowa + * Copyright (c) 2008-2018, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -64,6 +64,16 @@ class FunctionBase; struct Request { + // how many input headers can be put to in_headers struct + static const size_t MAX_INPUT_HEADERS = 32; + + // how many characters there can be in one header name + static const size_t INPUT_HEADER_NAME_MAX_LENGTH = 64; + + // how many characters there can be in one header value + static const size_t INPUT_HEADER_VALUE_MAX_LENGTH = 8192; + + /* request id is incremented for each request and is never 0 @@ -97,7 +107,7 @@ struct Request the HTTP method !! IMPROVE ME add the rest methods here */ - enum Method { get, post, head, unknown_method } method; + enum Method { get, post, head, delete_, unknown_method } method; /* @@ -116,6 +126,15 @@ struct Request PostFileTab post_file_tab; CookieTab cookie_tab; + // input headers (without cookies) + // at the moment we are using FastCGI and HTTP headers are prefixed with 'HTTP_' string + // so we drop the prefix and change all characters to small ones + + // although https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 says that there can be more + // than one http header with the same name we do not support it + // each header has a different name here, cookies we have in a different container (cookie_tab) + PT::Space headers_in; + /* html anchor (those part of URI after '#' character) @@ -209,7 +228,7 @@ struct Request // send as attachment (causes generating header: content-disposition: attachment) bool send_as_attachment; - // headers send to the client (without cookies) + // headers send to the client (without cookies) (may change to headers_out?) PT::Space out_headers; // cookies send to the client diff --git a/winixd/core/version.h b/winixd/core/version.h index 1fa561f..117c6e5 100644 --- a/winixd/core/version.h +++ b/winixd/core/version.h @@ -42,7 +42,7 @@ namespace Winix #define WINIX_VER_MAJOR 0 #define WINIX_VER_MINOR 6 -#define WINIX_VER_REVISION 5 +#define WINIX_VER_REVISION 6 diff --git a/winixd/functions/functionbase.cpp b/winixd/functions/functionbase.cpp index a2da4fa..c458530 100644 --- a/winixd/functions/functionbase.cpp +++ b/winixd/functions/functionbase.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2010-2016, Tomasz Sowa + * Copyright (c) 2010-2018, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -136,6 +136,10 @@ void FunctionBase::MakeGet() // do nothing by default } +void FunctionBase::MakeDelete() +{ + // do nothing by default +} diff --git a/winixd/functions/functionbase.h b/winixd/functions/functionbase.h index 637360e..7a9f347 100644 --- a/winixd/functions/functionbase.h +++ b/winixd/functions/functionbase.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2010-2016, Tomasz Sowa + * Copyright (c) 2010-2018, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -84,6 +84,7 @@ public: virtual bool HasAccess(); virtual void MakePost(); virtual void MakeGet(); + virtual void MakeDelete(); void SetConfig(Config * pconfig); void SetCur(Cur * pcur); diff --git a/winixd/functions/functions.cpp b/winixd/functions/functions.cpp index 1d68ace..25fd7a6 100644 --- a/winixd/functions/functions.cpp +++ b/winixd/functions/functions.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2010-2016, Tomasz Sowa + * Copyright (c) 2010-2018, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -449,6 +449,12 @@ void Functions::MakeFunction() // we should make a page similar like in a GET request but the content should not be returned only } else + if( cur->request->method == Request::delete_ ) + { + if( cur->request->redirect_to.empty() ) + cur->request->function->MakeDelete(); + } + else { log << log1 << "Functions: unknown request method (skipping)" << logend; }