/* * This file is a part of Winix * and is distributed under the 2-Clause BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2008-2021, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #ifndef headerfile_winix_core_request #define headerfile_winix_core_request #include #include #include #include "requesttypes.h" #include "models/item.h" #include "error.h" #include "config.h" #include "textstream.h" #include "templates/htmltextstream.h" #include "date/date.h" #include "space/space.h" #include "textstream/textstream.h" #include "outstreams.h" #include "models.h" #include "models/winixmodel.h" #include "header.h" namespace Winix { class FunctionBase; class Request : public WinixModel { public: // 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 (from -1 will be incremented to one) it's used for some optimizations e.g. in templates */ size_t id; /* * request start time * * start_time is the same as timespec_req_start.tv_sec * start_date is a pt::Date converted from start_time * */ timespec timespec_req_start; time_t start_time; pt::Date start_date; /* * request stop time * */ timespec timespec_req_stop; /* * request stop time - start time * */ timespec timespec_req_diff; /* * start time of the ezc engine (html templates) * */ timespec timespec_ezc_engine_start; /* * end time of the ezc engine (html templates) * */ timespec timespec_ezc_engine_stop; /* * * * * variables representing input from client's browser * * * */ /* the HTTP method !! IMPROVE ME add the rest methods here */ enum Method { get, post, head, delete_, 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; pt::Space post_in; bool is_postin_used;// temporarily, before all post variables will be put to post_in // 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) */ std::wstring anchor; // environment variables std::wstring env_request_method; std::wstring env_request_uri; std::wstring env_http_cookie; std::wstring env_http_host; std::wstring env_http_user_agent; std::wstring env_http_accept_encoding; std::wstring env_fcgi_role; std::wstring env_content_type; std::wstring env_https; // current IP address of the remote host // (read either from REMOTE_ADDR environment variable or from config.proxy_ip_header HTTP variable if config.check_proxy_ip_header is set to true) // (at the moment only IPv4 are supported) int ip; std::wstring ip_str; // ip_str can be ipv6 now // 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; // true if the request is being made by ajax by htmx library bool is_htmx_request; /* request input variables representing the winix filesystem */ // current directory std::vector 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) 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 * * * */ // the algorithm how a request's answer is created // ------------------------------------------------------------------------------------------ // // at the beginning of a request winix sets // answer_source to models // answer_container to text // use_ezc_engine to true // // next answer_container and use_ezc_engine can be changed in the following way: // // 1. winix will look for 'Accept' http header and depending on the header winix will set: // // Accept | answer_container | use_ezc_engine // ------------------------------------|----------------- // application/json | json | false // application/xml | xml | false // text/csv | csv | false // // // // 2. next answer_container is set depending on 'container' url parameter // container | answer_container // --------------------------------------------------------- // not present | don't change the value // text | text // json | json // xml | xml // csv | csv // // use_ezc_engine is set depending on 'answer' url parameter: // answer | use_ezc_engine // --------------------------------- // not present | don't change the value // html | true // data | false // // if 'answer' is html then we take into account two more parameters: // frame: frame_name (empty default) - if set then winix returns this specific frame // allframes: (if present then winix returns all frames) // // // // // // // the whole algorithm how the answer is created is shown below: // // // answer_source: bin_stream // |--------------->-------------- send out_bin_stream // | // | // | // | // | answer_source: models and use_ezc_engine: true // |------------------------>------------------------ // | | // | use ezc engine // | for converting models // | to out_main_stream and frame_streams // | | // | change answer_source to // | frame_stream (if there is 'allframes' parameter or 'frame' parameter is not empty) // | or to main_stream otherwise // | | // |-------------------------<----------------------- // | // | // | // | // | // | answer_source: main_stream // |----------------------->------------------------- // | | // | depending on answer_container // | | // | -------------------------------------------------------------------- // | | | | | // | text json xml csv // | | | | | // | send send send send // | out_main_stream out_main_stream out_main_stream out_main_stream // | as json text in one cell in first csv cell // | (without making e.g. // | an object) // | e.g. "text" text // | // | // | // | // | answer_source: frame_streams // |-------->------- // | | // | | // | depending on // | 'frame' string variable and 'allframes' // | | // | | // | | is 'frame' string empty or there is 'allframes' parameter // | |----------------------->--------------------- // | | | // | | depending on answer_container // | | | // | | -------------------------------------------------------------------- // | | | | | | // | | text json xml csv // | | | | | | // | | send text serialize serialize serialize // | | from all frames all frames all frames all frames // | | one by one to json to xml to csv // | | // | | // | | // | | // | | is 'frame' string not empty and there is no 'allframes' parameter // | |----------------------->--------------------- // | | // | depending on answer_container // | | // | -------------------------------------------------------------------- // | | | | | // | text json xml csv // | | | | | // | send text serialize serialize serialize // | from one frame one frame one frame one frame // | to json to xml to csv // | // | // | // | // | // | // | answer_source: models // |------------------------>------------------------ // | // depending on answer_container // | // -------------------------------------------------------------------- // | | | | // text json xml csv // | | | | // serialize serialize models serialize models serialize models // models to to json to xml to csv // csv // but return // as text/plain // change maybe answer_bin_stream -> source_bin_stream? enum AnswerSource { answer_bin_stream, answer_models, answer_main_stream, answer_frame_streams, }; // change maybe answer_text -> container_text? enum AnswerContainer { answer_text, answer_json, answer_xml, answer_csv, }; AnswerSource answer_source; AnswerContainer answer_container; bool use_ezc_engine; std::wstring frame; bool send_all_frames; // 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) (may change to headers_out?) 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; // binary page sent to the client if answer_source is answer_bin_stream BinaryPage out_bin_stream; // main text output stream where the html otput is generated from ezc templates // here the whole html page (with doctype, head, body) is generated HtmlTextStream out_main_stream; // text output streams used in ajax requests // in ezc templates you can use [ezc frame "stream_name"] or just [frame "stream_name"] keyword // to switch between streams Ezc::OutStreams out_streams; // models to return or to render through ezc library Ezc::Models models; // filter html content with HTMLFilter, default the same as config.html_filter bool use_html_filter; // if this variable is true then winix always return 200 OK header // when the status would be 404 (not found) or 403 (permission denied) // default: false bool use_200_status_for_not_found_and_permission_denied; // options used by ezc generators bool gen_trim_white; bool gen_skip_new_line; bool gen_use_special_chars; // index template name std::wstring html_template; /* additional variables used for common uses */ // DEPRECATED will be removed // usually items in the current directory (depends on the function) std::vector item_tab; Request(); void SetConfig(Config * pconfig); void fields(); void RequestStarts(); void RequestEnds(); void Clear(); bool PrepareAnswerType(); bool CheckContainerParameter(); bool CheckAnswerParameter(); bool IsParam(const wchar_t * 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 std::wstring & param_name); // returns an empty string if there is no such a parameter std::wstring * ParamValuep(const wchar_t * param_name); // returns nullptr if there is no such a parameter std::wstring * ParamValuep(const std::wstring & param_name); // returns nullptr if there is no such a parameter bool IsPostVar(const wchar_t * 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 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 std::wstring & var, std::wstring & result); std::wstring * PostVarp(const wchar_t * var); std::wstring * PostVarp(const std::wstring & var); bool AllPostVarEmpty(); // returning true if all post vars are empty // 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 void AddCookie(const NameType & name, const ValueType & value, pt::Date * expires = 0); template void AddCookie(const NameType & name, const ValueType & value, pt::Date & expires); private: Config * config; // used in ParamValue() and PostVar() when there is no such a param const std::wstring str_empty; void ClearOutputStreams(); void current_dir(morm::Wrapper & wrapper); void last_item_wrapper(morm::Wrapper & wrapper); MORM_MEMBER_FIELD(Request) }; template void 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) */ out_cookies.add_stream(name, cookie); } template void Request::AddCookie(const NameType & name, const ValueType & value, pt::Date & expires) { AddCookie(name, value, &expires); } } // namespace Winix #endif