vinegar.request_handler.file
Request handler that serves files from the file systems.
The files can either be served as is or they can be processed through a template engine. The handler can either work with a single file or with a whole directory of files.
Optionally, this handler can supply system data from a DataSource when
rendering templates. In this case, the request path has to contain a piece of
information that allows for identifying the system. This can either be the
system ID, or a value that allows for looking up the system ID through the
data source’s find_system method. This is configured through the
lookup_key configuration option.
The request handler classes provided by this module implement the
DataSourceAware interface in order to get access to the data source. When
using the request handlers in a container context, the container will usually
take care of injecting the data source. When using the classes directly, the
using code has to take care of that. However, a DataSource is only needed
when using the lookup_key configuration option. If that option is empty
(the default), no data source is needed and it is not even used if it is
available.
This module provides request handlers for both HTTP and TFTP. These two handlers are almost identical in their behavior with only a few minor differences:
The options related to the
Content-Typeheader are only available in the HTTP version. TFTP does not have any equivalent of theContent-Typeheader in HTTP.The TFTP version of the handler allows request paths that do not start with a forward slash (
/). In this case, the forward slash is added automatically. The HTTP version of the handler, in contrast, does not do anything like this because request paths not starting with a forward slash are rejected by the HTTP server component.
By default, this request handler does not process files as templates and treats
them as binary files. However, when enabling templating through the
template option, files are read as text files and decoded as UTF-8. The
result of the rendering process of the template engine is then encoded as UTF-8
before sending it to the client.
This means that the default value of the content_type option (only
available in the HTTP version of the handler) depends on whether a template
engine is used or not.
Request path matching
The request paths for which a handler is used are configured through its
request_path option. The configured request path must always start with a
/ and must not end with a / (the exception being the case where the
whole path just consists of a single /).
When operating in file mode (if the file configuration option is used), the
request path must be an exact match. For example, the configured request path
/my/file matches the actual request path /my/file (and my/file for
TFTP), but it does not match /my/file/ or /my/file/abc. In file mode,
the special request path / can only be used for the HTTP version of the
handler, not the TFTP one. The reason is that TFTP has no notion of index files
and due to a missing / being added automatically, such a configuration
would actually match requests with an empty path.
When operating in directory mode (if the root_dir configuration option is
used), the configured request path specifies the prefix that must match. Extra
portions of the actual request path are treated as the path of the requested
file relative to the root_dir. For example, if request_path is set to
/myprefix and root_dir is set to /path/on/fs, a request specifying
the path /myprefix/some/file would result in the file
/path/on/fs/some/file being served.
A request handler operating in directory mode will catch all requests that start with the specified prefix, even if the file does not actually exist. This means that a request handler serving a more specific path prefix has to come earlier in the chain of request handlers or it will never be asked whether it can handle a request.
When using the lookup_key configuration option, the configured request path
has to contain exactly one placeholder. The default placeholder string is
..., but this can be changed through the lookup_value_placeholder
configuration option.
When such a configuration is used, the placeholder marks a portion of the
request path that might be different from request to request. For example, a
configured request_path of /prefix/file-... will match an actual
request path of /prefix/file-abc, /prefix/file-def, etc. The
placeholder will not match an empty string, so the request path will not match
/prefix/file-.
The value that replaces the placeholder in the actual request path is
transformed according to the configuration set through the
lookup_value_transform configuration option and then used to find a system
ID through DataSource.find_system. This is most useful when also using a
template engine (see Using templates). If no matching
system can be found, the request file is treated as not existing (unless
lookup_no_result_action is set to continue).
Placeholders do not necessarily have to appear at the end of a request path.
For example, a request_path of /prefix/.../suffix` will match
/prefix/abc/suffix
Placeholders can be used in file mode as well as in directory mode. Like request paths that do not use place holders, the extra portion of the path will be used as the path of the file inside the directory, when operating in directory mode.
Using templates
Optionally, files served by the request handlers can be treated as templates.
This behavior is enabled by setting the template configuration option to
the name of one of the template engines supported by
get_template_engine. One good choice might be the jinja
template engine.
A context object with the name request_info is passed to the template
engine. For more information about this object, please refer to
Request information.
If the lookup_key configuration option is also set, the request handler
additionally provides two context objects to the template engine: The id
object contains the system ID (as a str). The data object contains the
data that has been returned from the data source’s get_data
method. The data object is passed as a
SmartLookupDict to make it easier to get nested
values.
The data object is not available if the data source’s get_data method
raised an exception. Usually, this will cause the template not even to be
rendered, but this can be overridden through the data_source_error_action
configuration option.
If the data source’s find_system method returns None and
lookup_no_result_action is set to continue, neither id nor data
are available. The same applies if find_system raises an exception and
lookup_no_result_action is set to continue and
data_source_error_action is set to ignore or warn.
Request information
The request_info object is a dict, which contains the following keys.
The headers and method keys are only present for requests received via
HTTP. Additional keys might be added in future versions.
client_addressThe value for this key is a two-tuple, where the first element is the IP address of the requesting client (as a
str) and the second element is the port number used by the client (as anint).headersThe HTTP request headers as an instance of
http.client.HTTPMessage.This key is only available for HTTP requests.
methodThe HTTP request method (e.g.
GET,POST, etc.).This key is only available for HTTP requests.
server_addressThe value for this key is a two-tuple, where the first element is the IP address on which the server received the request (as a
str) and the second element is the port number on which the server received the requeset (as anint).Please note that for TFTP requests, the server might not be able to determine the address on which a request was received (due to limitations on some platforms). In this case, this will instead be the IP address to which the server’s socket has been bound.
uriThe full request URI specified by the client in undecoded form. For a TFTP request, this typically is the filename. For an HTTP request, this might also include a query string.
Access restrictions
By default, this request handler will allow any client to retrieve files rendered for any system. This might not be desirable if the files contain confidential information (e.g. password), in particular when the network in which the Vinegar server is running cannot be deemed secure.
Therefore, it is possible to limit access to the data for each system to
specific clients. This is achieved through the client_address_key
configuration option. This option specifies a key into the system data. The key
can consist of multiple components separated by colons (:) to point into
a nested dictionary.
For example, suppose that client_address_key is set to net:ip_addr. If
the handler gets a request for the system ID myid, it will ask the data
source for the system data for this system by calling
data_source.get_data('myid', {}, '').
If the system data returned for this system does not contain a value for the specified key, the request is denied with HTTP status code 403 (forbidden).
For this example, let us assume that the data source returns the following data
for the system myid (expressed as YAML):
net:
ip_addr: 192.0.2.1
In this case, the request handler will only allow the request, if it comes from the IP address 192.0.2.1. In all other cases, it will reject the request.
Instead of a single IP address, the system data may also contain a list or set
of IP addresses. For example, if get_data returned the following system
data
net:
ip_addr:
- 192.0.2.1
- 2001:db8::1
the request would be allowed if it came from 192.0.2.1 or 2001:db8::1.
In addition to single IP addresses, IP subnets using CIDR notation are allowed as well. For example,
net:
ip_addr:
- 192.0.2.0/24
- 2001:db8::/64
will allow access for all clients from the IP subnets 192.0.2.0/24 and 2001:db8::/64.
As an alternative to the client_address_key option, the
client_address_list option may be specified. This option takes a list of IP
addresses or IP subnets and allows requests from clients that match one of
these entries. This can be useful in order to allow full access from certain
administrative clients.
When both client_address_key and client_address_list are specified,
they are combined, meaning that a request is allowed when it either matches one
of the entries from client_address_list or the entries from the data tree
for the client_address_key.
When the client_address_key option is used, the lookup_key option must
be set as well and this request handler needs a data source in order to get
information about the system. This request handler implements the
DataSourceAware interface, so when it is used inside a container, that
container will typically take care of setting the data source. If instantiated
directly, the data source has to be set explicitly by calling the handler’s
set_data_source method.
Configuration example
In order to understand how the various configuration options work together, we look at an example configuration (expressed in YAML):
lookup_key: 'net:mac_addr'
lookup_no_result_action: continue
lookup_value_transform:
- mac_address.normalize
request_path: '/prefix/...'
root_dir: '/path/to/the/directory'
template: jinja
We also assume that there is a file /path/to/the/directory/myconf.txt with
the following content:
{%- if id is not defined -%}
This content is for systems which we do not know.
{%- else -%}
This system has the ID {{ id }} and the MAC address
{{ data.get('net:mac_addr') }}.
{%- endif -%}
When a request for /prefix/02-03-04-05-06-0a/myconf.txt arrives, the MAC
address in the URL will be extracted and normalized (the - characters will
be replaced by : and letters will be converted to upper case). The
resulting value (02:03:04:05:06:0A) will be used as a lookup value together
and passed to the data source’s find_system method along with the lookup
key (net:mac_addr). Assuming that the data source can resolve this
key-value combination, the returned system ID is used in a call to get_data
and both the system ID and the data are passed to the template engine when
rendering myconf.txt.
As we have set the lookup_no_result_action to continue a MAC address
that is not known by the data source will not result in a “not found” error
(status code 404 in the case of HTTP). Instead, the template is rendered
without a system ID and system data, so that some default content is returned.
Configuration options
The file request handlers have several configuration options that can be
used to control their behavior. Most of them apply both the HTTP and TFTP
handler, but some are specific to only one of the two.
For a more detailed discussion about the options controlling request path matching, please refer to Request path matching. More information about the templating mechanism can be found in Using templates and a example making use of some of the options an be found in Configuration example.
The common options are:
file(mandatory):Path to the file that is served by the request handler. Either this option or the
root_diroption must be set. Both options cannot be used together. If this option is set, the request handler only answers requests that specify the exactrequest_pathand it does not serve any other files.request_path(mandatory):request path for which this request handler should be used. The specified path must start with a
/and it must not end with a/(unless the/is the only character in the whole path). When operating in directory mode, the value of this option is treated as a prefix. If thelookup_keyoption is set, the specified path must contain a placeholder (which is configured through thelookup_value_placeholderoption). Please refer to Request path matching for a more thorough discussion of request path matching.root_dir(mandatory):Path to the directory that contains the files that are served by the request handler. Either this option or the
fileoption must be set. Both options cannot be used together. If this option is set, the request handler answers all requests that specify a path that starts with therequest_path. The parts of the request path after that prefix are interpreted as the path of the file to be served, relative to theroot_dir.client_address_key(optional):Key into the system data that points to the place in the data where the allowed client address or addresses are stored. If this option is not set (the default), each client can access this handler for arbitrary system IDs and thus potentially retrieve data associated with these systems. When this is not desired, this option can be used to limit the allowe client (IP) addresses for each system. The key can point into a nested dictionary, using the colon (
:) to separate key components for the various levels. The value can be a string (matching exactly one IP address or IP subnet) or a list or set of IP addresses or IP subnets (matching any of the addresses or subnets in the list or set). If this option is set,lookup_keymust be set as well. Please refer to Access restrictions for a more detailed discussion of how this option can be used.client_address_list(optional):List of IP addresses or IP subnets from which requests are allowed. Please refer to Access restrictions for a more detailed discussion of how this option can be used.
data_source_error_action(optional):Action to be taken if the data source’s
find_systemorget_datamethod raises an exception. If set toerror(the default), the request handler lets this exception bubble up, typically resulting in the exception being logged and an error message being returned to the client. If set toignorethe exception is caught and process continues without the data from the data source (depending on thelookup_no_result_actioniffind_systemraised the exception). If set towarn, the same actions as for the theignoreaction are taken, but the exception is also logged.file_suffix(optional):File name suffix that is added to the requested file name when operating in directory mode. For example, when this is set to
.xyzand the file name in the request URI isabc.txt, the handler looks for the fileabc.txt.xyzon the file system. By default, this option is set to the empty string, so that the file name on the file system must be the same one as in the request. Setting this option to a non-empty value is particularly useful when using a template engine, so that the files on the file system can use a suffix appropriate for this template engine, helping editors with using the correct type of syntax highlighting. This option can only be used in combination with theroot_diroption, not with thefileoption.lookup_key(optional):Name of the lookup key that shall be used when calling
find_system. IfNoneor empty (the default), no system-specifc data is retrieved and therequest_pathis not expected to contain a placeholder. If set to the special value:system_id:, the value extracted from the request path is not passed tofind_system, but used as a system ID directly.lookup_no_result_action(optional):Action to be taken if no system ID can be determined. This can happen if
find_systemreturnsNoneor if it raises an exception anddata_source_error_actionis set toignoreorwarn. If set tocontinuethe code proceeds without information about the system, so neither theidnor thedataobject are available when rendering the template. If set tonot_found(the default), a “not found” error (error code 404 in the case of HTTP) os returned to the client.lookup_value_placeholder(optional):Placeholder that is used for the lookup value in the
request_pathoption. The default placeholder is.... This option has no effect unlesslookup_keyis set.lookup_value_transform(optional):Transformations to be applied to the lookup value. This is a list of transformations that are applied to the value extracted from the request path and being passed to
find_systemor being used as a system ID directly. This list is passed tovinegar.transform.get_transformation_chain. If this list is empty (the default), no transformations are applied and the string extracted from the request path is used as is.template(optional):name of the template engine (as a
str) that shall be used for rending the files. This name is passed toget_template_enginein order to retrieve the template engine. If empty or set toNone(the default), templating is disabled.template_config(optional):configuration for the template engine. The default is an empty dictionary (
{}). This configuration is passed on to the template engine as is.
The configuration options specific to the HTTP version of the request handler are:
content_type(optional):Value to use for the
Content-Typeheader that is sent to the client. IfNoneor empty (the default), the content_type is guessed based on thetemplateoption. If no template engine is used, the valueapplication/octet-streamis used. If a template engine is used, the valuetext/plain; charset=UTF-8is used. When specifying a different type, the value should include acharsetspecification for text types. The output of a template engine is always encoded asUTF-8, so typically this charset should be specified.content_type_map(optional):Dictionary of filenames and file extensions to
Content-Typespecifications. This option is similar to thecontent_typeoption, but it allows for using different values for different files served by the same handler. When serving a file, the handler first looks for a key that exactly matches the filename (without any preceding path components). If no such key is found, it looks for a key that matches the file extension. If no such key is found either, it uses the value of thecontent_typeoption. For example, when serving the file/path/to/my.html, the handler first looks formy.htmland, if that key does not exist, tries.html. This option can only be used in directory mode. In file mode, using this option does not make sense because there only is a single file and thus using thecontent_typeoption is simpler.
- class vinegar.request_handler.file.HttpFileRequestHandler(config: Mapping[Any, Any])
HTTP request handler that serves files from the file system.
For information about the configuration options supported by this request handler, please refer to the
module documentation.- can_handle(uri: str, context: Any) bool
Tell whether the request can be handled by this request handler.
Returns
Trueif the request can be handled andFalseif it cannot be handled and the next request handler should be tried.- Parameters:
uri – URI that has been requested by the client. The URI includes the full request path including the query string (if present) and has not been URL decoded.
context – context object that was returned by
prepare_context.
- Returns:
Trueif this request handler can handle the specified request,Falseif the request should be deferred to the next handler.
- handle(request_info: HttpRequestInfo, body: BufferedIOBase, context: Any) Tuple[HTTPStatus, Mapping[str, str] | None, BufferedIOBase | None]
Handle the request.
This method returns a tuple of three items. The first item is the HTTP status code, the second item are the headers that shall be sent to the client, and the third is a file-like object from which the data for the requested file can be read. The returned file-like object must supply its data in binary form.
- Parameters:
request_info – information about the request that shall be handled.
body – file-like object that provides the request body sent by the client. This file-like object returns
byteswhen reading.context – context object that was returned by
prepare_context.
- Returns:
tuple of the HTTP status code, the response headers, and a file-like object that provides the data that is transferred to the client. The file-like object must provide binary data. The response headers may be
None, which has the same effect as supplying an empty dict (no headers are added to the response). The file-like object may also beNone, which means that the body of the response is empty. Typically, this is only useful when indicating an error (status code >= 400). In this case, a body with an appropriate error message for the status code is generated by the server.
- prepare_context(uri: str) Any
Prepare a context object for use by
can_handleandhandle. This method is called for each request before callingcan_handle.This is useful when both function need to do some processing on the URI. This processing can be implemented in
prepare_contextand passed to the two other methods through the context so that it does not have to be done twice.The return value of this method is passed to
can_handleandhandle. The default implementation simply returnsNone.- Parameters:
uri – URI that has been requested by the client. The URI includes the full request path including the query string (if present) and has not been URL decoded.
- Returns:
context object that is passed to
can_handleandhandle.
- class vinegar.request_handler.file.TftpFileRequestHandler(config: Mapping[Any, Any])
TFTP request handler that serves files from the file system.
For information about the configuration options supported by this request handler, please refer to the
module documentation.- can_handle(filename: str, context: Any) bool
Tell whether the request can be handled by this request handler.
Returns
Trueif the request can be handled andFalseif it cannot be handled and the next request handler should be tried.- Parameters:
filename – filename that has been requested by the client.
context – context object that was returned by
prepare_context.
- Returns:
Trueif this request handler can handle the specified request,Falseif the request should be deferred to the next handler.
- handle(filename: str, client_address: Tuple[str, int] | Tuple[str, int, int, int], server_address: Tuple[str, int] | Tuple[str, int, int, int], context: Any) BufferedIOBase
Handle the request. This method returns a file-like object from which the data for the requested file can be read. The returned file-like object must supply its data in binary form.
If the request handler detects that it actually cannot send data to the client (e.g. because the client lacks the required permissions), it should signal that by raising a
TftpError.- Parameters:
filename – filename that has been requested by the client.
client_address – client address. The structure of the tuple depends on the address family in use, but typically the first element is the client’s host address and the second element is the client’s port number.
server_address – server address. The structure of the tuple depends on the address family in use, but typically the first element is the server’s host address and the second element is the server’s port number. Please note that determining the actual address specified by the client might not be possible if the sever binds to all interfaces (
::) and the platform does not support theIPV6_PKTINFOsocket option. In this case, this will be the address to which the server is bound and not the address on which the request was actually received.context – context object that was returned by
prepare_context.
- Returns:
file-like object that provides the data that is transferred to the client. The file-like object must provide binary data.
- prepare_context(filename: str) Any
Prepare a context object for use by
can_handleandhandle. This method is called for each request before callingcan_handle.This is useful when both function need to do some processing on the filename or client address. This processing can be implemented in
prepare_contextand passed to the two other methods through the context so that it does not have to be done twice.The return value of this method is passed to
can_handleandhandle. The default implementation simply returnsNone.- Parameters:
filename – filename that has been requested by the client.
- Returns:
context object that is passed to
can_handleandhandle.
- vinegar.request_handler.file.get_instance_http(config: Mapping[Any, Any]) HttpFileRequestHandler
Create a HTTP request handler serving files.
If the request handler needs a data source (if its
lookup_keyconfiguration option is used), the data source has to be set by calling theset_data_sourcemethod of the returned object.- Parameters:
config – configuration for this request handler. Please refer to the
module documentationfor a list of supported options.- Returns:
HTTP request handler serving files from the file system.
- vinegar.request_handler.file.get_instance_tftp(config: Mapping[Any, Any]) TftpFileRequestHandler
Create a TFTP request handler serving files.
If the request handler needs a data source (if its
lookup_keyconfiguration option is used), the data source has to be set by calling theset_data_sourcemethod of the returned object.- Parameters:
config – configuration for this request handler. Please refer to the
module documentationfor a list of supported options.- Returns:
TFTP request handler serving files from the file system.