/* * 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_http_accept; std::wstring env_http_accept_language; 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 container is selected is shown below: // (the whole answer's algorightm is implemented in PrepareAnswerType() method) // ------------------------------------------------------------------------------------------ // // at the beginning we set container_type to "raw" meaning simple text or html, then // we check the "Accept" http header, if it is set then we set container_type accordingly: // // Accept | container_type // ----------------------------------------------- // text/html | container_raw // application/xhtml+xml | container_raw // application/json | container_json // application/xml | container_xml // text/csv | container_csv // // next we check "container" url parameter, if it is set then we set container_type accordingly // ("container" url parameter has higher precedence than "Accept" http header): // // container | container_type // ----------------------------------------------- // raw | container_raw // json | container_json // xml | container_xml // csv | container_csv // // // Samples: // // http://domain.tld/dir/controller // returns html answer from the main ezc stream // // http://domain.tld/dir/controller/container:raw // returns html answer from the main ezc stream (the same as above) // // http://domain.tld/dir/controller/frame:abc // returns "abc" frame as html // // http://domain.tld/dir/controller/container:json // returns all serialized models to json and no ezc streams // // http://domain.tld/dir/controller/container:xml // returns all serialized models to xml and no ezc streams // // http://domain.tld/dir/controller/container:json/frame:abc,xyz // returns all serialized models to json and two frames in 'ezc_frames' object // // http://domain.tld/dir/controller/container:json/all_frames // returns all serialized models to json and all frames in 'ezc_frames' object // // http://domain.tld/dir/controller/container:json/main_stream // returns all serialized models and the main ezc stream in 'main_stream' field // // http://domain.tld/dir/controller/container:json/main_stream/all_frames // returns all serialized models to json, all frames and the main stream // bool send_bin_stream; bool send_main_stream; bool send_all_frames; std::vector send_frames; bool use_ezc_engine; bool serialize_models; // change maybe answer_text -> container_text? enum ContainerType { container_raw, container_json, container_xml, container_csv, }; ContainerType container_type; // at the beginning those with higher priority std::vector accept_mime_types; // at the beginning those with higher priority std::vector accept_languages; // 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(); void PrepareAnswerType(); 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); bool has_frame(const wchar_t * frame); bool has_frame(const std::wstring & frame); private: Config * config; // used in ParamValue() and PostVar() when there is no such a param const std::wstring str_empty; void ClearOutputStreams(); void CheckAcceptHeader(); void CheckContainerParameter(); void PrepareFrameNames(); 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