Skip to content

Documentation

Overview

md.http.message component provides implementation of psr.http.message contracts and few useful tools out from box.

Architecture overview

Architecture overview

Install

pip install md.http.message --index-url https://source.md.land/python/

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 is 8
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.

Request identifier chaining

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).

Uri

Stream

iter_stream

Message

Request

UploadedFile

ServerRequest

Response