Files
winix/winixd/core/request.h
Tomasz Sowa f7b5ac0dc8 change the way how winix answer is created
Now we can return ezc content and models serialized in the same json structure,
Xml and Csv are not implemented yet.
Ezc frames are returned in 'ezc_frames' field.
Main ezc stream is returned in 'main_stream' field.
Frame url parameter can take more than one frame (names separated by commas).
Honor Accept http header (AcceptParser).

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 (not implemented yet)

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
2022-02-01 18:44:23 +01:00

531 lines
14 KiB
C++

/*
* This file is a part of Winix
* and is distributed under the 2-Clause BSD licence.
* Author: Tomasz Sowa <t.sowa@ttmath.org>
*/
/*
* 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 <fcgiapp.h>
#include <sstream>
#include <vector>
#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_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<Item*> 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<std::wstring> 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<HeaderValue> accept_mime_types;
// 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<HtmlTextStream, true> 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> 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<typename NameType, typename ValueType>
void AddCookie(const NameType & name, const ValueType & value, pt::Date * expires = 0);
template<typename NameType, typename ValueType>
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<typename NameType, typename ValueType>
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<typename NameType, typename ValueType>
void Request::AddCookie(const NameType & name, const ValueType & value, pt::Date & expires)
{
AddCookie(name, value, &expires);
}
} // namespace Winix
#endif