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
requestFactorytoFileUploadRequest. - Sets
protocoltoWebSocketUpgradeHTTPChannel, so/vortexwscan upgrade. - Sets
sessionCookieNameSuffixfrom the site name and listen port. - Wraps the root in
FormBasedAuthSessionWrapperwhenenableLoginis true (the default). - Listens with TLS when
enableSslis true. - Starts an HTTP listener that redirects to the TLS port when
redirectFromHttpPortis 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>.
Check the cookie
- Restart the Twisted process after installing txhttputil 1.4.14 or later.
- Trigger a finished HTTP response that calls
request.getSession(). - Read
Set-Cookieon that response. - 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:
- A child resource.
- A file under a directory added with
addFileSystemRoot. - The single-page
index.html, when the last path segment has no dot. 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 filterFileUnderlayResourceTest: underlay files, directory traversal,HttpFileDownloaderRedirectToLoginPageResourceTest: login redirect and authorized rootSessionCookieUtilTest: 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.17
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| txhttputil-1.4.17.tar.gz | 51.2 kB | Without provenance | Details for txhttputil-1.4.17.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| txhttputil-1.4.17-py3-none-any.whl | Python 3 | none | any | Details for txhttputil-1.4.17-py3-none-any.whl |
Total release size: 112.1 kB
Release files / txhttputil-1.4.17.tar.gz
| Download URL | txhttputil-1.4.17.tar.gz |
|---|---|
| Size | 51.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9b03fe8b16dc9c2ed1464e23e734aa64c7b40740c2336d10cd92a9b0da5e354b
|
|
BLAKE2b-256 checksum How to use checksums |
254cd77e64965100ebcf7785f3a93e76240cb8b0ad6d5bd6459a90b0142c74fa
|
| 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.17-py3-none-any.whl
| Download URL | txhttputil-1.4.17-py3-none-any.whl |
|---|---|
| Size | 60.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
60f677d0eab8a29dbf5d6e9e3e058cb06c872b91d02631a2a02eb69fae5b9065
|
|
BLAKE2b-256 checksum How to use checksums |
3f174e4f6070066a6e11e3757bf07b0e1c1f0f117430589edd2a4729c05e3610
|
| 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.17
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.