Title: | Powerful Classes for HTTP Requests and Responses |
---|---|
Description: | In order to facilitate parsing of http requests and creating appropriate responses this package provides two classes to handle a lot of the housekeeping involved in working with http exchanges. The infrastructure builds upon the 'rook' specification and is thus well suited to be combined with 'httpuv' based web servers. |
Authors: | Thomas Lin Pedersen [cre, aut] |
Maintainer: | Thomas Lin Pedersen <[email protected]> |
License: | MIT + file LICENSE |
Version: | 0.2.5.9000 |
Built: | 2024-10-11 04:44:44 UTC |
Source: | https://github.com/thomasp85/reqres |
This list matches the most normal mime types with their respective formatters
using default arguments. For a no-frills request parsing this can be supplied
directly to Response$format()
. To add or modify to this list simply supply
the additional parsers as second, third, etc, argument and they will
overwrite or add depending on whether it specifies a mime type already
present.
default_formatters
default_formatters
formatters for an overview of the build in formatters in reqres
## Not run: res$format(default_formatters, 'text/plain' = format_plain(sep = ' ')) ## End(Not run)
## Not run: res$format(default_formatters, 'text/plain' = format_plain(sep = ' ')) ## End(Not run)
This list matches the most normal mime types with their respective parsers
using default arguments. For a no-frills request parsing this can be supplied
directly to Request$parse()
. To add or modify to this list simply supply
the additional parsers as second, third, etc, argument and they will
overwrite or add depending on whether it specifies a mime type already
present.
default_parsers
default_parsers
parsers for an overview of the build in parsers in reqres
## Not run: req$parse(default_parsers, 'application/json' = parse_json(flatten = TRUE)) ## End(Not run)
## Not run: req$parse(default_parsers, 'application/json' = parse_json(flatten = TRUE)) ## End(Not run)
This set of functions can be used to construct formatting functions adhering to the Response$format() requirements.
format_json( dataframe = "rows", matrix = "rowmajor", Date = "ISO8601", POSIXt = "string", factor = "string", complex = "string", raw = "base64", null = "list", na = "null", auto_unbox = FALSE, digits = 4, pretty = FALSE, force = FALSE ) format_plain(sep = "\n") format_xml(encoding = "UTF-8", options = "as_xml") format_html(encoding = "UTF-8", options = "as_html") format_table(...)
format_json( dataframe = "rows", matrix = "rowmajor", Date = "ISO8601", POSIXt = "string", factor = "string", complex = "string", raw = "base64", null = "list", na = "null", auto_unbox = FALSE, digits = 4, pretty = FALSE, force = FALSE ) format_plain(sep = "\n") format_xml(encoding = "UTF-8", options = "as_xml") format_html(encoding = "UTF-8", options = "as_html") format_table(...)
dataframe |
how to encode data.frame objects: must be one of 'rows', 'columns' or 'values' |
matrix |
how to encode matrices and higher dimensional arrays: must be one of 'rowmajor' or 'columnmajor'. |
Date |
how to encode Date objects: must be one of 'ISO8601' or 'epoch' |
POSIXt |
how to encode POSIXt (datetime) objects: must be one of 'string', 'ISO8601', 'epoch' or 'mongo' |
factor |
how to encode factor objects: must be one of 'string' or 'integer' |
complex |
how to encode complex numbers: must be one of 'string' or 'list' |
raw |
how to encode raw objects: must be one of 'base64', 'hex' or 'mongo' |
null |
how to encode NULL values within a list: must be one of 'null' or 'list' |
na |
how to print NA values: must be one of 'null' or 'string'. Defaults are class specific |
auto_unbox |
automatically |
digits |
max number of decimal digits to print for numeric values. Use |
pretty |
adds indentation whitespace to JSON output. Can be TRUE/FALSE or a number specifying the number of spaces to indent. See |
force |
unclass/skip objects of classes with no defined JSON mapping |
sep |
The line separator. Plain text will be split into multiple strings based on this. |
encoding |
The character encoding to use in the document. The default encoding is ‘UTF-8’. Available encodings are specified at http://xmlsoft.org/html/libxml-encoding.html#xmlCharEncoding. |
options |
default: ‘format’. Zero or more of
|
... |
parameters passed on to |
A function accepting an R object
parsers for converting Request
bodies into R objects
default_formatters for a list that maps the most common mime types to their respective formatters
fake_rook <- fiery::fake_request( 'http://example.com/test', content = '', headers = list( Content_Type = 'text/plain', Accept = 'application/json, text/csv' ) ) req <- Request$new(fake_rook) res <- req$respond() res$body <- mtcars res$format(json = format_json(), csv = format_table(sep=',')) res$body # Cleaning up connections rm(fake_rook, req, res) gc()
fake_rook <- fiery::fake_request( 'http://example.com/test', content = '', headers = list( Content_Type = 'text/plain', Accept = 'application/json, text/csv' ) ) req <- Request$new(fake_rook) res <- req$respond() res$body <- mtcars res$format(json = format_json(), csv = format_table(sep=',')) res$body # Cleaning up connections rm(fake_rook, req, res) gc()
This set of functions can be used to construct parsing functions adhering to the Request$parse() requirements.
parse_json( simplifyVector = TRUE, simplifyDataFrame = simplifyVector, simplifyMatrix = simplifyVector, flatten = FALSE ) parse_plain(sep = "\n") parse_xml(encoding = "", options = "NOBLANKS", base_url = "") parse_html( encoding = "", options = c("RECOVER", "NOERROR", "NOBLANKS"), base_url = "" ) parse_multiform() parse_queryform() parse_table(...)
parse_json( simplifyVector = TRUE, simplifyDataFrame = simplifyVector, simplifyMatrix = simplifyVector, flatten = FALSE ) parse_plain(sep = "\n") parse_xml(encoding = "", options = "NOBLANKS", base_url = "") parse_html( encoding = "", options = c("RECOVER", "NOERROR", "NOBLANKS"), base_url = "" ) parse_multiform() parse_queryform() parse_table(...)
simplifyVector |
coerce JSON arrays containing only primitives into an atomic vector |
simplifyDataFrame |
coerce JSON arrays containing only records (JSON objects) into a data frame |
simplifyMatrix |
coerce JSON arrays containing vectors of equal mode and dimension into matrix or array |
flatten |
automatically |
sep |
The line separator. Plain text will be split into multiple strings based on this. |
encoding |
Specify a default encoding for the document. Unless otherwise specified XML documents are assumed to be in UTF-8 or UTF-16. If the document is not UTF-8/16, and lacks an explicit encoding directive, this allows you to supply a default. |
options |
Set parsing options for the libxml2 parser. Zero or more of
|
base_url |
When loading from a connection, raw vector or literal html/xml, this allows you to specify a base url for the document. Base urls are used to turn relative urls into absolute urls. |
... |
parameters passed on to |
A function accepting a raw vector and a named list of directives
formatters for converting Response
bodies into compatible types
default_parsers for a list that maps the most common mime types to their respective parsers
fake_rook <- fiery::fake_request( 'http://example.com/test', content = '[1, 2, 3, 4]', headers = list( Content_Type = 'application/json' ) ) req <- Request$new(fake_rook) req$parse(json = parse_json()) req$body # Cleaning up connections rm(fake_rook, req) gc()
fake_rook <- fiery::fake_request( 'http://example.com/test', content = '[1, 2, 3, 4]', headers = list( Content_Type = 'application/json' ) ) req <- Request$new(fake_rook) req$parse(json = parse_json()) req$body # Cleaning up connections rm(fake_rook, req) gc()
This class wraps all functionality related to extracting information from a
http request. Much of the functionality is inspired by the Request class in
Express.js, so the documentation
for this will complement this document. As reqres
is build on top of the
Rook specifications
the Request
object is initialized from a Rook-compliant object. This will
often be the request object provided by the httpuv
framework. While it
shouldn't be needed, the original Rook object is always accessible and can be
modified, though any modifications will not propagate to derived values in
the Request
object (e.g. changing the HTTP_HOST
element of the Rook
object will not change the host
field of the Request
object). Because of
this, direct manipulation of the Rook object is generally discouraged.
as.Request(x, ...) is.Request(x)
as.Request(x, ...) is.Request(x)
x |
An object coercible to a |
... |
Parameters passed on to |
A Request
object (for as.Request()
) or a logical indicating whether
the object is a Request
(for is.Request()
)
A new 'Request'-object is initialized using the new()
method on the
generator:
Usage
req <- Request$new(rook, trust = FALSE)
|
Arguments
rook |
The rook request that the new object should wrap | |
trust |
Is this request trusted blindly. If TRUE X-Forwarded-* headers will be returned when querying host, ip, and protocol
|
The following fields are accessible in a Request
object:
trust
A logical indicating whether the request is trusted. Mutable
method
A string indicating the request method (in lower case, e.g. 'get', 'put', etc.). Immutable
body
An object holding the body of the request. This is an empty
string by default and needs to be populated using the set_body()
method
(this is often done using a body parser that accesses the Rook$input
stream). Immutable
cookies
Access a named list of all cookies in the request. These have been URI decoded. Immutable
headers
Access a named list of all headers in the request. In order
to follow R variable naming standards -
have been substituted with _
.
Use the get_header()
method to lookup based on the correct header name.
Immutable
host
Return the domain of the server given by the "Host" header if
trust == FALSE
. If trust == true
returns the X-Forwarded-Host
instead.
ip
Returns the remote address of the request if trust == FALSE
.
if trust == TRUE
it will instead return the first value of the
X-Forwarded-For
header. Immutable
ips
If trust == TRUE
it will return the full list of ips in the
X-Forwarded-For
header. If trust == FALSE
it will return an empty
vector. Immutable
protocol
Returns the protocol (e.g. 'http') used for the request.
If trust == TRUE
it will use the value of the X-Forwarded-Proto
header.
Immutable
root
The mount point of the application receiving this request. Can be empty if the application is mounted on the server root. Immutable
path
The part of the url following the root. Defines the local target of the request (independent of where it is mounted). Immutable
url
The full URL of the request. Immutable
query
The query string of the request (anything following "?" in the URL) parsed into a named list. The query has been url decoded and "+" has been substituted with space. Multiple queries are expected to be separated by either "&" or "|". Immutable
querystring
The unparsed query string of the request, including
"?". If no query string exists it will be ""
rather than "?"
xhr
A logical indicating whether the X-Requested-With
header
equals XMLHttpRequest
thus indicating that the request was performed using
a JavaScript library such as jQuery. Immutable
secure
A logical indicating whether the request was performed using
a secure connection, i.e. protocol == 'https'
. Immutable
origin
The original object used to create the Request
object. As
reqres
currently only works with rook this will always return the original
rook object. Immutable, though the content of the rook object itself might
be manipulated as it is an environment.
response
If a Response
object has been created for this request
it is accessible through this field. Immutable
The following methods are available in a Request
object:
set_body(content)
Sets the content of the request body. This method
should mainly be used in concert with a body parser that reads the
rook$input
stream
set_cookies(cookies)
Sets the cookies of the request. The cookies are automatically parsed and populated, so this method is mainly available to facilitate cookie signing and encryption
get_header(name)
Get the header of the specified name.
accepts(types)
Given a vector of response content types it returns
the preferred one based on the Accept
header.
accepts_charsets(charsets)
Given a vector of possible character
encodings it returns the preferred one based on the Accept-Charset
header.
accepts_encoding(encoding)
Given a vector of possible content
encodings (usually compression algorithms) it selects the preferred one
based on the Accept-Encoding
header. If there is no match it will return
"identity"
signaling no compression.
accepts_language(language)
Given a vector of possible content
languages it selects the best one based on the Accept-Language
header.
is(type)
Queries whether the body of the request is in a given
format by looking at the Content-Type
header. Used for selecting the best
parsing method.
respond()
Creates a new Response
object from the request
parse(..., autofail = TRUE)
Based on provided parsers it selects
the appropriate one by looking at the Content-Type
header and assigns the
result to the request body. A parser is a function accepting a raw vector,
and a named list of additional directives,
and returns an R object of any kind (if the parser knows the input to be
plain text, simply wrap it in rawToChar()
). If the body is compressed, it
will be decompressed based on the Content-Encoding
header prior to passing
it on to the parser. See parsers for a list of pre-supplied parsers.
Parsers are either supplied in a named list or as named arguments to the
parse method. The names should correspond to mime types or known file
extensions. If autofail = TRUE
the response will be set with the correct
error code if parsing fails. parse()
returns TRUE
if parsing was
successful and FALSE
if not
parse_raw(autofail = TRUE)
This is a simpler version of the
parse()
method. It will attempt to decompress the body and set the body
field to the resulting raw vector. It is then up to the server to decide how
to handle the payload. It returns TRUE
if successful and FALSE
otherwise.
as_message()
Prints a HTTP representation of the request to the output stream.
new()
Request$new(rook, trust = FALSE)
print()
Request$print(...)
set_body()
Request$set_body(content)
set_cookies()
Request$set_cookies(cookies)
accepts()
Request$accepts(types)
accepts_charsets()
Request$accepts_charsets(charsets)
accepts_encoding()
Request$accepts_encoding(encoding)
accepts_language()
Request$accepts_language(language)
is()
Request$is(type)
get_header()
Request$get_header(name)
respond()
Request$respond()
parse()
Request$parse(..., autofail = TRUE)
parse_raw()
Request$parse_raw(autofail = TRUE)
as_message()
Request$as_message()
clone()
The objects of this class are cloneable with this method.
Request$clone(deep = FALSE)
deep
Whether to make a deep clone.
Response
for handling http responses
fake_rook <- fiery::fake_request( 'http://example.com/test?id=34632&question=who+is+hadley', content = 'This is an elaborate ruse', headers = list( Accept = 'application/json; text/*', Content_Type = 'text/plain' ) ) req <- Request$new(fake_rook) # Get full URL req$url # Get list of query parameters req$query # Test if content is text req$is('txt') # Perform content negotiation for the response req$accepts(c('html', 'json', 'txt')) # Cleaning up connections rm(fake_rook, req) gc()
fake_rook <- fiery::fake_request( 'http://example.com/test?id=34632&question=who+is+hadley', content = 'This is an elaborate ruse', headers = list( Accept = 'application/json; text/*', Content_Type = 'text/plain' ) ) req <- Request$new(fake_rook) # Get full URL req$url # Get list of query parameters req$query # Test if content is text req$is('txt') # Perform content negotiation for the response req$accepts(c('html', 'json', 'txt')) # Cleaning up connections rm(fake_rook, req) gc()
This class handles all functionality involved in crafting a http response.
Much of the functionality is inspired by the Request class in Express.js, so
the documentation for this will
complement this document. As reqres
is build on top of the
Rook specifications
the Response
object can be converted to a compliant list object to be
passed on to e.g. the httpuv
handler.
## S3 method for class 'Response' as.list(x, ...) is.Response(x)
## S3 method for class 'Response' as.list(x, ...) is.Response(x)
x |
A |
... |
Ignored |
A Response
object is always created
as a response to a Request
object and contains a reference to the
originating Request
object. A Response
is always initialized with a
404 Not Found code, an empty string as body and the Content-Type
header set
to text/plain
. As the Content-Type
header is required for httpuv
to
function, it will be inferred if missing when converting to a list. If the
body is a raw vector it will be set to application/octet-stream
and
otherwise it will be set to text/plain
. It is always advised to consciously
set the Content-Type
header though. The only exception is when attaching a
standard file where the type is inferred from the file extension
automatically. Unless the body is a raw vector it will automatically be
converted to a character vector and collapsed to a single string with "\n"
separating the individual elements before the Response
object is converted
to a list (that is, the body can exist as any type of object up until the
moment where the Response
object is converted to a list). To facilitate
communication between different middleware the Response
object contains
a data store where information can be stored during the lifetime of the
response.
A rook-compliant list-response (in case of as.list()
) or a logical
indicating whether the object is a Response
(in case of is.Response()
)
A new 'Response'-object is initialized using the new()
method on the
generator:
Usage
res <- Response$new(request)
|
But often it will be provided by the request using the respond()
method,
which will provide the response, creating one if it doesn't exist
Usage
res <- request$respond()
|
Arguments
request |
The Request object that the Response is responding to |
|
The following fields are accessible in a Response
object:
status
Gets or sets the status code of the response. Is initialised
with 404L
body
Set or get he body of the response. If it is a character
vector with a single element named 'file'
it will be interpreted as the
location of a file. It is better to use the file
field for creating a
response referencing a file as it will automatically set the correct
headers.
file
Set or get the location of a file that should be used as the
body of the response. If the body is not referencing a file (but contains
something else) it will return NULL
. The Content-Type
header will
automatically be inferred from the file extension, if known. If unknown it
will defaults to application/octet-stream
. If the file has no extension it
will be text/plain
. Existence of the file will be checked.
type
Get or sets the Content-Type
header of the response based on
a file extension or mime-type.
request
Get the original Request
object that the object is
responding to.
The following methods are available in a Response
object:
set_header(name, value)
Sets the header given by name
. value
will be converted to character. A header will be added for each element in
value
. Use append_header()
for setting headers without overwriting
existing ones.
get_header(name)
Returns the header(s) given by name
remove_header(name)
Removes all headers given by name
has_header(name)
Test for the existence of any header given by
name
append_header(name, value)
Adds an additional header given by
name
with the value given by value
. If the header does not exist yet it
will be created.
set_data(key, value)
Adds value
to the internal data store and
stores it with key
get_data(key)
Retrieves the data stored under key
in the internal
data store.
remove_data(key)
Removes the data stored under key
in the
internal data store.
has_data(key)
Queries whether the data store has an entry given by
key
attach(file, filename=basename(file), type=NULL)
Sets the body to
the file given by file
and marks the response as a download by setting the
Content-Disposition
to attachment; filename=<filename>
. Use the type
argument to overwrite the automatic type inference from the file extension.
status_with_text(code)
Sets the status to code
and sets the body
to the associated status code description (e.g. Bad Gateway
for 502L
)
set_cookie(name, value, encode = TRUE, expires = NULL, http_only = NULL, max_age = NULL, path = NULL, secure = NULL, same_site = NULL)
Adds
the cookie given by name
to the given value
, optionally url encoding it,
along with any additional directives. See
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie for a
description of the different directives. If the cookie already exists it
will be overwritten. The validity of the directives will automatically be
checked. expires
expects a POSIXct object, http_only
and secure
expect
a logical, max_age
expects an integer, path
a string, and same_site
either "Lax"
or "Strict"
remove_cookie(name)
Removes the cookie named name
from the
response.
has_cookie(name)
Queries whether the response contains a cookie
named name
set_links(...)
Sets the Link
header based on the named arguments
passed to ...
. The names will be used for the rel
directive.
format(..., autofail = TRUE, compress = TRUE)
Based on the
formatters passed in through ...
content negotiation is performed with
request and the preferred formatter is chosen. The Content-Type
header is
set automatically. If compress = TRUE
the
compress()
method will be called after formatting. If an error is
encountered and autofail = TRUE
the response will be set to 500
. If a
formatter is not found and autofail = TRUE
the response will be set to
406
. If formatting is successful it will return TRUE
, if not it will
return FALSE
compress(priority = c('gzip', 'deflate', 'br', 'identity'))
Based
on the provided priority, an encoding is negotiated with the request and
applied. The Content-Encoding
header is set to the chosen compression
algorithm.
content_length()
Calculates the length (in bytes) of the body.
This is the number that goes into the Content-Length
header. Note that the
Content-Length
header is set automatically by httpuv
so this method
should only be called if the response size is needed for other reasons.
as_list()
Converts the object to a list for further processing by
a Rook compliant server such as httpuv
. Will set Content-Type
header if
missing and convert a non-raw body to a single character string.
new()
Response$new(request)
print()
Response$print(...)
set_header()
Response$set_header(name, value)
get_header()
Response$get_header(name)
remove_header()
Response$remove_header(name)
has_header()
Response$has_header(name)
append_header()
Response$append_header(name, value)
set_data()
Response$set_data(key, value)
get_data()
Response$get_data(key)
remove_data()
Response$remove_data(key)
has_data()
Response$has_data(key)
timestamp()
Response$timestamp()
attach()
Response$attach(file, filename = basename(file), type = NULL)
status_with_text()
Response$status_with_text(code)
set_cookie()
Response$set_cookie( name, value, encode = TRUE, expires = NULL, http_only = NULL, max_age = NULL, path = NULL, secure = NULL, same_site = NULL )
remove_cookie()
Response$remove_cookie(name)
has_cookie()
Response$has_cookie(name)
set_links()
Response$set_links(...)
format()
Response$format(..., autofail = TRUE, compress = TRUE)
compress()
Response$compress(priority = c("gzip", "deflate", "br", "identity"))
content_length()
Response$content_length()
as_list()
Response$as_list()
as_message()
Response$as_message()
clone()
The objects of this class are cloneable with this method.
Response$clone(deep = FALSE)
deep
Whether to make a deep clone.
Request
for handling http requests
fake_rook <- fiery::fake_request( 'http://example.com/test?id=34632&question=who+is+hadley', content = 'This is elaborate ruse', headers = list( Accept = 'application/json; text/*', Content_Type = 'text/plain' ) ) req <- Request$new(fake_rook) res <- Response$new(req) res # Set the body to the associated status text res$status_with_text(200L) res$body # Infer Content-Type from file extension res$type <- 'json' res$type # Prepare a file for download res$attach(system.file('DESCRIPTION', package = 'reqres')) res$type res$body res$get_header('Content-Disposition') # Cleaning up connections rm(fake_rook, req, res) gc()
fake_rook <- fiery::fake_request( 'http://example.com/test?id=34632&question=who+is+hadley', content = 'This is elaborate ruse', headers = list( Accept = 'application/json; text/*', Content_Type = 'text/plain' ) ) req <- Request$new(fake_rook) res <- Response$new(req) res # Set the body to the associated status text res$status_with_text(200L) res$body # Infer Content-Type from file extension res$type <- 'json' res$type # Prepare a file for download res$attach(system.file('DESCRIPTION', package = 'reqres')) res$type res$body res$get_header('Content-Disposition') # Cleaning up connections rm(fake_rook, req, res) gc()
Dates/times in HTTP headers needs a specific format to be valid, and is furthermore always given in GMT time. These two functions aids in converting back and forth between the required format.
to_http_date(time, format = NULL) from_http_date(time)
to_http_date(time, format = NULL) from_http_date(time)
time |
A string or an object coercible to POSIXct |
format |
In case |
to_http_date()
returns a properly formatted string, while
from_http_date()
returns a POSIXct object
time <- to_http_date(Sys.time()) time from_http_date(time)
time <- to_http_date(Sys.time()) time from_http_date(time)