Skip to main content

TxHttpUtil

Flying Ion helpers for serving a site with twisted.web. Requires Python 3.9 or later.

Site

setupSite listens, wraps the root resource, and configures the request class.

from txhttputil.site.SiteUtil import setupSite

setupSite("Peek Office Site", rootResource, portNum=8002)

setupSite does all of the following:

  • Sets requestFactory to FileUploadRequest.
  • Sets protocol to WebSocketUpgradeHTTPChannel, so /vortexws can upgrade.
  • Sets sessionCookieNameSuffix from the site name and listen port.
  • Wraps the root in FormBasedAuthSessionWrapper when enableLogin is true (the default).
  • Listens with TLS when enableSsl is true.
  • Starts an HTTP listener that redirects to the TLS port when redirectFromHttpPort is set.
setupSite(
    "Peek Office Site",
    rootResource,
    portNum=8002,
    enableLogin=False,
    enableSsl=True,
    sslBundleFilePath="/path/to/key-cert-cachain.pem",
    redirectFromHttpPort=8080,
)

Pass enableProtocolNegotiation=False when several TLS listeners share one OpenSSL context. That uses SiteWithoutProtocolNegotiation, which skips ALPN setup on each new connection.

Mutual TLS uses the same PEM helpers as a hand-built listener:

setupSite(
    "Peek Platform Data Exchange",
    rootResource,
    portNum=8011,
    enableLogin=False,
    enableSsl=True,
    sslBundleFilePath="/path/to/server.pem",
    sslEnableMutualTLS=True,
    sslMutualTLSCertificateAuthorityBundleFilePath="/path/to/root-cas.pem",
    sslMutualTLSTrustedPeerCertificateBundleFilePath="/path/to/peers.pem",
    dhParamPemFilePath="/path/to/dhparam.pem",
)

Session cookies

Browsers store cookies by host name. They do not store them by port. Two Twisted processes on localhost share one cookie jar when both use TWISTED_SESSION or TWISTED_SECURE_SESSION.

FileUploadRequest.getSession appends a suffix when the site sets sessionCookieNameSuffix. The suffix is <serviceSlug>_<port>. The slug is the service name in lower case. Each run of non-alphanumeric characters becomes _.

Listener Cookie name
HTTP TWISTED_SESSION_<serviceSlug>_<port>
TLS TWISTED_SECURE_SESSION_<serviceSlug>_<port>

Examples: TWISTED_SESSION_peek_office_site_8002, TWISTED_SECURE_SESSION_attuneops_8443.

The port in the name is the Twisted listen port. A webpack dev server port such as 4202 is not used. The dev server proxies to the Twisted port.

A bare TWISTED_SESSION or TWISTED_SECURE_SESSION means the suffix was not set, or the process is still running code from before txhttputil 1.4.14.

Build the site yourself

setupSite is optional. Set requestFactory and the suffix yourself. Set the suffix after listenTCP or listenSSL returns. Use port.getHost().port when the listen port is 0. That is the port the process actually bound.

from twisted.internet import reactor
from twisted.web import server

from txhttputil.site.FileUploadRequest import FileUploadRequest
from txhttputil.site.SessionCookieUtil import SessionCookieUtil
from txwebsocket.txws import WebSocketUpgradeHTTPChannel

site = server.Site(rootResource)
site.protocol = WebSocketUpgradeHTTPChannel
site.requestFactory = FileUploadRequest

port = yield reactor.listenSSL(
    httpsPort, site, contextFactory=sslContextFactory
)
site.sessionCookieNameSuffix = (
    SessionCookieUtil.buildSessionCookieNameSuffix(
        "AttuneOps", port.getHost().port
    )
)

On a TLS listener the cookie name is TWISTED_SECURE_SESSION_attuneops_<boundPort>.

  1. Restart the Twisted process after installing txhttputil 1.4.14 or later.
  2. Trigger a finished HTTP response that calls request.getSession().
  3. Read Set-Cookie on that response.
  4. Confirm the name includes the service slug and the Twisted listen port.

The WebSocket 101 preamble does not copy Set-Cookie from the request. A missing cookie on /vortexws does not show whether the suffix is set. Use a normal HTTP response that calls getSession.

Login

setupSite(..., enableLogin=True) wraps the root in FormBasedAuthSessionWrapper. Unauthenticated requests render LoginElement. A POST with username and password calls AuthCredentials.check. AllowAllAuthCredentials accepts any password.

This form login is not suitable for a public network.

RedirectToLoginPageResource is a site root for the same idea without that wrapper. Unauthorized paths redirect to loginPath. The login path renders loginResource. Authorized requests use authorizedRoot.

from txhttputil.site.RedirectToLoginPageResource import (
    RedirectToLoginPageResource,
)

root = RedirectToLoginPageResource(
    b"/login",
    loginResource,
    appRoot,
    isAuthorized=request_is_logged_in,
)

isAuthorized returns true when that request may see appRoot. Pass formLoginFactory to handle a POST of username and password before the redirect.

Resources

BasicResource is the resource-tree node. putChild registers a child. deleteChild removes one. Pass allowedIpsList to answer other client addresses with HTTP 403. Set isGzipped = True to gzip that resource.

FileUnderlayResource resolves a URL in this order:

  1. A child resource.
  2. A file under a directory added with addFileSystemRoot.
  3. The single-page index.html, when the last path segment has no dot.
  4. NoResource.

Paths that escape a filesystem root are rejected. addRedirectionRule maps one path to another. enableSinglePageApplication turns on the index fallback.

from txhttputil.site.FileUnderlayResource import FileUnderlayResource
from txhttputil.site.RedirectionRule import RedirectionRule

root = FileUnderlayResource()
root.addFileSystemRoot("/srv/app/dist")
root.enableSinglePageApplication("index.html")
root.addRedirectionRule(RedirectionRule("/old", "/new"))
root.putChild(b"api", apiResource)

StaticFileResource serves one file. It sets a content type, cache headers, and writes the body in chunks.

RedirectToHttpsResource redirects the same path to https on another port. setupSite uses it when redirectFromHttpPort is set.

Uploads

FileUploadRequest parses multipart/form-data. Each part is a SpooledNamedTemporaryFile. The part stays in memory until it passes 1 MB, or until name or fileno is used. It then rolls to a temporary file. setupSite installs this request class. A hand-built site must set site.requestFactory = FileUploadRequest.

A part larger than that limit can use more memory than 1 MB before the roll to disk.

HTTP client

HttpFileDownloader GETs a URL with Twisted Agent and returns a spooled file.

from txhttputil.downloader.HttpFileDownloader import HttpFileDownloader

tmpFile = yield HttpFileDownloader("https://example.com/file.bin").run()

HttpResourceProxy forwards the incoming request to another host and writes the response back. Set useSsl and the mutual TLS bundle paths to authenticate to that host.

from txhttputil.downloader.HttpResourceProxy import HttpResourceProxy

proxy = HttpResourceProxy("10.1.1.8", 8011, useSsl=True)
root.putChild(b"upstream", proxy)

TLS material

parsePemBundleForServer reads a server key and certificate chain. parsePemBundleForClient reads a client chain. parseTrustRootFromBundle reads CA certificates. parsePemBundleForTrustedPeers reads peer certificates. parseDiffieHellmanParameter reads DH parameters. generateDiffieHellmanParameterBytes writes a new DH file.

buildCertificateOptionsForTwisted turns a parsed server bundle into Twisted CertificateOptions. buildSSLContextFactoryForMutualTLS builds the client context used by HttpResourceProxy.

Other helpers

deferToThreadWrap runs a blocking function on the reactor thread pool and logs failures. maybeDeferredWrap turns a synchronous return value into a Deferred. vortexLogFailure logs a Failure once.

txhttputil.compat.cgiCompat parses multipart/form-data and Content-Type headers. The stdlib cgi module is gone in Python 3.13. FileUploadRequest uses this module.

filterModules yields importable module names in a package directory. It skips __init__ and names that end in Test.

Tests

Trial tests live next to the modules they cover:

  • BasicResourceTest: child tree, render, IP filter
  • FileUnderlayResourceTest: underlay files, directory traversal, HttpFileDownloader
  • RedirectToLoginPageResourceTest: login redirect and authorized root
  • SessionCookieUtilTest: cookie suffix and cookie name

Run them from the repository root:

python -m twisted.trial txhttputil.site.BasicResourceTest txhttputil.site.FileUnderlayResourceTest txhttputil.site.RedirectToLoginPageResourceTest txhttputil.site.SessionCookieUtil_test

python txhttputil_manual_test_server.py listens on port 8000 as txHttpUtil Test Siteport.

License

Copyright (c) 2016-2026 Flying Ion Pty Ltd. Released under the MIT License.

Metadata

Release files for txhttputil 1.4.16

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for txhttputil 1.4.16
File Size Uploaded Provenance Actions
txhttputil-1.4.16.tar.gz 51.4 kB Without provenance Details for txhttputil-1.4.16.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for txhttputil 1.4.16
File Interpreter ABI Platform Actions
txhttputil-1.4.16-py3-none-any.whl Python 3 none any Details for txhttputil-1.4.16-py3-none-any.whl

Total release size: 112.3 kB

Release files / txhttputil-1.4.16.tar.gz

Download URL txhttputil-1.4.16.tar.gz
Size 51.4 kB
Tags Source
SHA-256 checksum
How to use checksums
cdf3d99c6f1322beafd690eac19da43d6a7b2cc83617ac76560c533c4bc387a2
BLAKE2b-256 checksum
How to use checksums
1757c5daaf72bed9dac7fcae163a7fb51a347f027dd1436c6c53aebbd3f55ef4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.13

Release files / txhttputil-1.4.16-py3-none-any.whl

Download URL txhttputil-1.4.16-py3-none-any.whl
Size 61.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5020fbe5a32697fa5111a12d34866fe40c4d707357f860d40b08656d38bc468e
BLAKE2b-256 checksum
How to use checksums
2cf23f53e13ca36bf4536c70af1612e3f44fb5af391b8365d76af061d71f8192
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.13

Security for txhttputil 1.4.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.4.16 This release

2 release files No provenance

0.1.2

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page