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

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.15
File Size Uploaded Provenance Actions
txhttputil-1.4.15.tar.gz 50.6 kB Without provenance Details for txhttputil-1.4.15.tar.gz

Built distribution (wheel)

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

Total release size: 110.5 kB

Release files / txhttputil-1.4.15.tar.gz

Download URL txhttputil-1.4.15.tar.gz
Size 50.6 kB
Tags Source
SHA-256 checksum
How to use checksums
c842bd344051b5c43d8a97bbb54074022f0dee4bddfc24e0a3d797c6d2b7b56e
BLAKE2b-256 checksum
How to use checksums
3d92709fff374afeefe21ee3618e475aa79c59f0a3fb3df768a00e9e283dde33
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.15-py3-none-any.whl

Download URL txhttputil-1.4.15-py3-none-any.whl
Size 59.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a002c549ebcae5b76d0e4ff67f21f55b3cb715d8aa6a10f4c6324ed2299602c3
BLAKE2b-256 checksum
How to use checksums
de5a617c313d3cb2faf1a74f89739d93df18cb267d393dd3dbcf3a72f3592c81
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.15

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