Documentation
Overview
md.http.message component provides implementation of psr.http.message contracts and few useful tools out from box.
Architecture overview
Install
Usage
Response code statuses
HTTP code statuses constants are defined with CODE_
prefix, there are few examples:
import md.http.message
if __name__ == '__main__':
print(md.http.message.CODE_CONTINUE)
print(md.http.message.CODE_SWITCHING_PROTOCOLS)
print(md.http.message.CODE_PROCESSING)
# ...
For mapping code status to reason phrase there is constant MAP_CODE_REASON_PHRASE
:
import md.http.message
if __name__ == '__main__':
print(md.http.message.MAP_CODE_REASON_PHRASE[md.http.message.CODE_CONTINUE]) # 'Continue'
print(md.http.message.MAP_CODE_REASON_PHRASE[md.http.message.CODE_SWITCHING_PROTOCOLS]) # 'Switching Protocols'
print(md.http.message.MAP_CODE_REASON_PHRASE[md.http.message.CODE_PROCESSING]) # 'Processing'
# ...
Request identifier
Request identifier is a generated unique string for each request.
Info
Collision or a complete match of values is acceptable, while it is not in the same time.
Typically, it is used for analytics and debug purposes, to explicitly relate any application action to concrete request processing, for example, in log record context.
Set request identifier
SetRequestIdentifier
class is designed to obtain unique
identifier and inject it into server request attribute.
Info
By design, set or unset action is performed by low-level application on request processing start, for high-level API it is for accessing only. See Application section below for details.
Warning
Server request attributes set logic is not a part of psr.http.message
standard. Accessing attributes
property of psr.http.message.ServerRequestInterface
implementation may raise an AttributeError
exception.
Warning
SetRequestIdentifier.for_
method is not a pure and not idempotent function.
It obtains a random value using IdentifierGeneratorInterface
(see below)
and overwrite request.attributes['id']
value each time.
Warning
Do not confuse request.get_headers().get('x-request-id')
and attributes['id']
import psr.http.server
import psr.http.message
import md.http.message
if __name__ == '__main__':
# arrange
set_request_identifier = md.http.message.SetRequestIdentifier(
identifier_generator=md.http.message.AlphabetIdentifierGenerator()
)
server_request = ... # assume request is formed
assert(server_request, psr.http.message.ServerRequestInterface)
# act
if not hasattr(server_request, 'attributes'): # optionally init server request attributes
server_request.attributes = {} # set
set_request_identifier.for_(request=server_request)
# assert
assert 'id' in server_request.attributes
print(server_request.attributes['id'])
Identifier generator strategy
IdentifierGeneratorInterface
contract is designed to generate unique identifier.
Warning
Keep in mind that long request identifier (either chained or not) may flood logs, if it exposed for each record while request is processing. Try to keep identifier length about 8 characters.
String identifier
AlphabetIdentifierGenerator
is implementation of IdentifierGeneratorInterface
contract designed
to generate identifier value as random string.
Constructor of this class takes optional alphabet
and length
arguments:
alphabet
- set of characters to be used in generated string, default is ascii characters in lower-case.length
- length of generated string, default is8
import md.http.message
if __name__ == '__main__':
string_generator = md.http.message.AlphabetIdentifierGenerator()
print(string_generator.generate()) # e.g. zlom0cqk
string_generator = md.http.message.AlphabetIdentifierGenerator(length=16)
print(string_generator.generate()) # e.g. 3h6dtfei20ivm0b9
string_generator = md.http.message.AlphabetIdentifierGenerator(alphabet='ab42', length=16)
print(string_generator.generate()) # e.g. 4a44b2a4a424b2a2
UUID identifier
UUIDGenerator
is another implementation of IdentifierGeneratorInterface
contract designed
to generate identifier value in uuid4
format.
import md.http.message
if __name__ == '__main__':
full_uuid_generator = md.http.message.UUIDGenerator()
print(full_uuid_generator.generate()) # e.g. ab9c8c11-8902-4fbb-8d8f-a549b20f324d
Request identifier chaining
While request is processing, application may send request to another application and so forth. Just like that, an application could receive such nested request.
To make these requests traceable across few applications, there is request identifier chaining feature. It makes possible to retrieve all logs records made while requests were processed, for example to detect at which point was an issue or some significant event.
When client provides X-Request-Id
value in request headers, this values is used
to form a final request identifier, roughly f'{client_request_id} {app_generated_id}'
.
Formally, chaining requests is concatenation its identifiers.
By default, in SetRequestIdentifier
action this feature is enabled, to disable it,
set chain_request
parameter in constructor to False
, for example:
import md.http.message
if __name__ == '__main__':
set_request_identifier = md.http.message.SetRequestIdentifier(
identifier_generator=md.http.message.AlphabetIdentifierGenerator(),
chain_request=False # ... here
)
Info
Request chainig feature is applicable not only for HTTP message processing lifecycle, but also for the other application ports (e.g. event-bus, notification-bus and etc).