PEP 444 – Python Web3 Interface
- Autor:
- Chris McDonough <chrism at plope.com>, Armin Ronacher <armin.ronacher at active-4.com>
- Discussions-To:
- Web-SIG Liste
- Status:
- Verschoben
- Typ:
- Informational
- Erstellt:
- 19-Jul-2010
Inhaltsverzeichnis
- Zusammenfassung
- PEP Verschiebung
- Begründung und Ziele
- Unterschiede zu WSGI
- Übersicht der Spezifikation
- Details der Spezifikation
- Implementierungs-/Anwendungsnotizen
- Offene Fragen
- Diskussionspunkte
- WSGI 1.0 Kompatibilität
- Environ und Response-Werte als Bytes
- Anwendungen sollten
web3.inputüberCONTENT_LENGTHhinaus lesen dürfen web3.inputUnbekannte Längeread()vonweb3.inputsollte No-Size Calling Convention unterstützenheadersals literale Liste von Two-Tuples- Entfernte Anforderung, dass Middleware nicht blockieren darf
web3.script_nameundweb3.path_info- Lange Response-Header
- Request-Trailer und Chunked Transfer Encoding
- Referenzen
- Urheberrecht
Zusammenfassung
Dieses Dokument beschreibt eine vorgeschlagene Schnittstelle der zweiten Generation zwischen Webservern und Python-Webanwendungen oder Frameworks.
PEP Verschiebung
Die weitere Erforschung der in diesem PEP behandelten Konzepte wurde mangels eines aktuellen Vertreters, der an der Förderung der Ziele des PEP interessiert ist und Feedback sammelt und einarbeitet, sowie über ausreichend verfügbare Zeit zur effektiven Umsetzung, zurückgestellt.
Beachten Sie, dass seit der ersten Erstellung dieses PEP PEP 3333 als inkrementelles Update erstellt wurde, das die Verwendung von WSGI unter Python 3.2+ erlaubt. Eine alternative Spezifikation, die die Python 3-Ziele einer saubereren Trennung von binären und Textdaten weiterverfolgt, könnte jedoch immer noch wertvoll sein.
Begründung und Ziele
Dieses Protokoll und diese Spezifikation sind stark von der Web Services Gateway Interface (WSGI) 1.0-Norm beeinflusst, die in PEP 333 beschrieben ist. Die allgemeine Begründung für eine Standardnorm, die die Interoperabilität von Python-basierten Webservern und Anwendungen ermöglicht, wird in PEP 333 dargelegt. Dieses Dokument verwendet im Wesentlichen PEP 333 als Vorlage und ändert dessen Formulierungen an verschiedenen Stellen, um einen anderen Standard zu schaffen.
Python verfügt derzeit über eine breite Palette von Webanwendungsframeworks, die das WSGI 1.0-Protokoll verwenden. Aufgrund von Änderungen in der Sprache ist das WSGI 1.0-Protokoll jedoch nicht mit Python 3 kompatibel. Diese Spezifikation beschreibt ein standardisiertes WSGI-ähnliches Protokoll, das es Python 2.6, 2.7 und 3.1+ Anwendungen ermöglicht, mit Webservern zu kommunizieren. Web3 ist eindeutig ein WSGI-Derivat; es verwendet lediglich einen anderen Namen als „WSGI“, um anzuzeigen, dass es in keiner Weise abwärtskompatibel ist.
Anwendungen und Server, die nach dieser Spezifikation geschrieben sind, sollen unter Python 2.6.X, Python 2.7.X und Python 3.1+ korrekt funktionieren. Weder eine Anwendung noch ein Server, der die Web3-Spezifikation implementiert, kann leicht so geschrieben werden, dass er unter Python 2-Versionen vor 2.6 oder Python 3-Versionen vor 3.1 funktioniert.
Hinweis
Welche Python 3-Version http://bugs.python.org/issue4006 behoben hat, so dass os.environ['foo'] Surrogates (gemäß PEP 383) zurückgibt, wenn der Wert von ‚foo‘ nicht mit der aktuellen Locale dekodiert werden kann, anstatt mit einem KeyError fehlzuschlagen, ist die *wahre* minimale Python 3-Version. Insbesondere wird Python 3.0 nicht unterstützt.
Hinweis
Python 2.6 ist die erste Python-Version, die einen Alias für bytes und die b"foo" Literal-Syntax unterstützte. Deshalb ist es die Mindestversion, die von Web3 unterstützt wird.
Erklärbarkeit und Dokumentationsfähigkeit sind die Haupttreiber für die technischen Entscheidungen innerhalb des Standards.
Unterschiede zu WSGI
- Alle protokollspezifischen Umgebungsnamen sind mit
web3.anstattwsgi.präfixiert, z. B.web3.inputanstattwsgi.input. - Alle als Werte im Umgebungs-Dictionary vorhandenen Werte sind explizit *Byte*-Instanzen anstelle von nativen Strings. (Umgebungs-*Schlüssel* sind jedoch native Strings, immer
str, unabhängig von der Plattform). - Alle von einer Anwendung zurückgegebenen Werte müssen Byte-Instanzen sein, einschließlich Statuscode, Header-Namen und -Werte sowie des Body.
- Wo immer WSGI 1.0 auf ein
app_iterverwies, verweist diese Spezifikation auf einenbody. - Kein
start_response()Callback (und daher keinwrite()aufrufbares Objekt und keineexc_info-Daten). - Die Funktion
readline()vonweb3.inputmuss einen Größenhinweisparameter unterstützen. - Die Funktion
read()vonweb3.inputmuss durch Länge begrenzt sein. Ein Aufruf ohne Größenangabe darf nicht mehr lesen als der Content-Length-Header angibt. Wenn kein Content-Length-Header vorhanden ist, darf der Stream bei einem Lesevorgang nichts zurückgeben. Er darf niemals mehr Daten vom Client anfordern, als angegeben wurde. - Keine Anforderung, dass Middleware eine leere Zeichenkette liefert, wenn sie mehr Informationen von einer Anwendung benötigt, um Ausgabe zu erzeugen (z. B. keine „Middleware-Handhabung von Blockgrenzen“).
- An „file_wrapper“ übergebene dateiähnliche Objekte müssen eine
__iter__haben, die Bytes zurückgibt (niemals Text). wsgi.file_wrapperwird nicht unterstützt.QUERY_STRING,SCRIPT_NAME,PATH_INFOWerte, die vom Server in environ platziert werden müssen (jeder als leere Byte-Instanz, wenn kein zugehöriger Wert in der HTTP-Anfrage empfangen wird).web3.path_infoundweb3.script_namesollten, wenn möglich, vom ursprünglichen Web3-Server in die Web3-Umgebung eingefügt werden. Wenn verfügbar, ist jeder der ursprüngliche, einfache 7-Bit-ASCII-URL-kodierte Varianten seines CGI-Äquivalents, direkt aus der Anfrage-URI abgeleitet (mit %2F-Segment-Markierungen und anderen Meta-Zeichen intakt). Wenn der Server einen (oder beide) dieser Werte nicht bereitstellen kann, muss er den/die nicht bereitstellbare(n) Wert(e) aus der Umgebung weglassen.- Diese Anforderung wurde entfernt: „Middleware-Komponenten **dürfen nicht** auf Iteration warten, um auf mehrere Werte von einem Anwendungs-Iterable zu warten. Wenn die Middleware mehr Daten von der Anwendung akkumulieren muss, bevor sie eine Ausgabe erzeugen kann, **muss** sie eine leere Zeichenkette liefern.“
SERVER_PORTmuss eine Byte-Instanz sein (keine Ganzzahl).- Der Server darf keinen zusätzlichen
Content-Length-Header durch Schätzen der Länge aus dem Response-Iterable einfügen. Dies muss in allen Situationen von der Anwendung selbst gesetzt werden. - Wenn der Ursprungsserver die Fähigkeit
web3.asyncbewirbt, darf ein von dem Server verwendbarer Web3-Anwendungs-Callable ein Callable zurückgeben, das keine Argumente akzeptiert. Wenn dies geschieht, soll dieses Callable periodisch vom Ursprungsserver aufgerufen werden, bis es eine nicht-None-Antwort zurückgibt, die eine normale Web3-Antwort-Tuple sein muss.
Übersicht der Spezifikation
Die Web3-Schnittstelle hat zwei Seiten: die „Server“- oder „Gateway“-Seite und die „Anwendungs“- oder „Framework“-Seite. Die Serverseite ruft ein Callable-Objekt auf, das von der Anwendungsseite bereitgestellt wird. Die Einzelheiten, wie dieses Objekt bereitgestellt wird, liegen beim Server oder Gateway. Es wird davon ausgegangen, dass einige Server oder Gateways vom Deployer einer Anwendung verlangen, ein kurzes Skript zu schreiben, um eine Instanz des Servers oder Gateways zu erstellen und sie mit dem Anwendungsobjekt zu versorgen. Andere Server und Gateways können Konfigurationsdateien oder andere Mechanismen verwenden, um anzugeben, woher ein Anwendungsobjekt importiert oder anderweitig bezogen werden soll.
Zusätzlich zu „reinen“ Servern/Gateways und Anwendungen/Frameworks ist es auch möglich, „Middleware“-Komponenten zu erstellen, die beide Seiten dieser Spezifikation implementieren. Solche Komponenten fungieren als Anwendung für ihren enthaltenden Server und als Server für eine enthaltene Anwendung und können verwendet werden, um erweiterte APIs, Inhalts-Transformationen, Navigation und andere nützliche Funktionen bereitzustellen.
In dieser Spezifikation werden wir den Begriff „Anwendungs-Callable“ für „eine Funktion, eine Methode oder eine Instanz mit einer __call__-Methode“ verwenden. Es liegt am Server, Gateway oder der Anwendung, die das Anwendungs-Callable implementiert, die geeignete Implementierungstechnik für ihre Bedürfnisse zu wählen. Umgekehrt darf ein Server, Gateway oder eine Anwendung, die ein Callable aufruft, **keine** Abhängigkeit davon haben, welche Art von Callable ihr zur Verfügung gestellt wurde. Anwendungs-Callables dürfen nur aufgerufen, nicht aber introspektiert werden.
Die Anwendungs-/Framework-Seite
Das Anwendungsobjekt ist einfach ein Callable-Objekt, das ein Argument akzeptiert. Der Begriff „Objekt“ sollte nicht als Anforderung einer tatsächlichen Objektinstanz missverstanden werden: eine Funktion, Methode oder eine Instanz mit einer __call__-Methode sind alle als Anwendungsobjekt verwendbar. Anwendungsobjekte müssen mehrfach aufrufbar sein, da praktisch alle Server/Gateways (außer CGI) solche wiederholten Anfragen stellen werden. Wenn dies durch die Implementierung der tatsächlichen Anwendung nicht garantiert werden kann, muss sie in eine Funktion eingepackt werden, die bei jedem Aufruf eine neue Instanz erstellt.
Hinweis
Obwohl wir uns auf ein „Anwendungs“-Objekt beziehen, sollte dies nicht so ausgelegt werden, dass Anwendungsentwickler Web3 als Web-Programmier-API verwenden werden. Es wird davon ausgegangen, dass Anwendungsentwickler weiterhin bestehende, hochrangige Framework-Dienste zur Entwicklung ihrer Anwendungen nutzen werden. Web3 ist ein Werkzeug für Framework- und Serverentwickler und ist nicht dazu bestimmt, Anwendungsentwickler direkt zu unterstützen.)
Ein Beispiel für eine Anwendung, die eine Funktion ist (simple_app)
def simple_app(environ):
"""Simplest possible application object"""
status = b'200 OK'
headers = [(b'Content-type', b'text/plain')]
body = [b'Hello world!\n']
return body, status, headers
Ein Beispiel für eine Anwendung, die eine Instanz ist (simple_app)
class AppClass(object):
"""Produce the same output, but using an instance. An
instance of this class must be instantiated before it is
passed to the server. """
def __call__(self, environ):
status = b'200 OK'
headers = [(b'Content-type', b'text/plain')]
body = [b'Hello world!\n']
return body, status, headers
simple_app = AppClass()
Alternativ kann ein Anwendungs-Callable ein Callable zurückgeben, anstatt eines Tuples, wenn der Server asynchrone Ausführung unterstützt. Siehe Informationen zu web3.async für weitere Informationen.
Die Server-/Gateway-Seite
Der Server oder das Gateway ruft das Anwendungs-Callable einmal pro empfangener Anfrage von einem HTTP-Client auf, die an die Anwendung gerichtet ist. Zur Veranschaulichung ist hier ein einfaches CGI-Gateway, implementiert als Funktion, die ein Anwendungsobjekt entgegennimmt. Beachten Sie, dass dieses einfache Beispiel eine eingeschränkte Fehlerbehandlung aufweist, da eine nicht abgefangene Ausnahme standardmäßig nach sys.stderr ausgegeben und vom Webserver protokolliert wird.
import locale
import os
import sys
encoding = locale.getpreferredencoding()
stdout = sys.stdout
if hasattr(sys.stdout, 'buffer'):
# Python 3 compatibility; we need to be able to push bytes out
stdout = sys.stdout.buffer
def get_environ():
d = {}
for k, v in os.environ.items():
# Python 3 compatibility
if not isinstance(v, bytes):
# We must explicitly encode the string to bytes under
# Python 3.1+
v = v.encode(encoding, 'surrogateescape')
d[k] = v
return d
def run_with_cgi(application):
environ = get_environ()
environ['web3.input'] = sys.stdin
environ['web3.errors'] = sys.stderr
environ['web3.version'] = (1, 0)
environ['web3.multithread'] = False
environ['web3.multiprocess'] = True
environ['web3.run_once'] = True
environ['web3.async'] = False
if environ.get('HTTPS', b'off') in (b'on', b'1'):
environ['web3.url_scheme'] = b'https'
else:
environ['web3.url_scheme'] = b'http'
rv = application(environ)
if hasattr(rv, '__call__'):
raise TypeError('This webserver does not support asynchronous '
'responses.')
body, status, headers = rv
CLRF = b'\r\n'
try:
stdout.write(b'Status: ' + status + CRLF)
for header_name, header_val in headers:
stdout.write(header_name + b': ' + header_val + CRLF)
stdout.write(CRLF)
for chunk in body:
stdout.write(chunk)
stdout.flush()
finally:
if hasattr(body, 'close'):
body.close()
Middleware: Komponenten, die beide Seiten bespielen
Ein einzelnes Objekt kann die Rolle eines Servers in Bezug auf einige Anwendungen spielen und gleichzeitig als Anwendung für einige Server fungieren. Solche „Middleware“-Komponenten können Funktionen wie z. B.
- Weiterleitung einer Anfrage an verschiedene Anwendungsobjekte basierend auf der Ziel-URL, nachdem die
environentsprechend umgeschrieben wurde. - Mehrere Anwendungen oder Frameworks nebeneinander im selben Prozess laufen lassen.
- Lastverteilung und Fernverarbeitung durch Weiterleitung von Anfragen und Antworten über ein Netzwerk.
- Durchführung von Content-Postprocessing, z. B. Anwenden von XSL-Stylesheets.
Die Anwesenheit von Middleware im Allgemeinen ist für die „Server/Gateway“- und die „Anwendungs/Framework“-Seiten der Schnittstelle transparent und erfordert keine spezielle Unterstützung. Ein Benutzer, der Middleware in eine Anwendung integrieren möchte, stellt die Middleware-Komponente einfach dem Server zur Verfügung, als wäre sie eine Anwendung, und konfiguriert die Middleware-Komponente so, dass sie die Anwendung aufruft, als wäre die Middleware-Komponente ein Server. Natürlich kann die „Anwendung“, die die Middleware umschließt, tatsächlich eine weitere Middleware-Komponente sein, die eine weitere Anwendung umschließt, und so weiter, wodurch ein sogenannter „Middleware-Stack“ entsteht.
Eine Middleware muss asynchrone Ausführung ermöglichen oder andernfalls deaktiviert werden.
Hier eine Middleware, die den HTTP_HOST-Schlüssel ändert, wenn ein X-Host-Header vorhanden ist, und allen HTML-Antworten einen Kommentar hinzufügt.
import time
def apply_filter(app, environ, filter_func):
"""Helper function that passes the return value from an
application to a filter function when the results are
ready.
"""
app_response = app(environ)
# synchronous response, filter now
if not hasattr(app_response, '__call__'):
return filter_func(*app_response)
# asynchronous response. filter when results are ready
def polling_function():
rv = app_response()
if rv is not None:
return filter_func(*rv)
return polling_function
def proxy_and_timing_support(app):
def new_application(environ):
def filter_func(body, status, headers):
now = time.time()
for key, value in headers:
if key.lower() == b'content-type' and \
value.split(b';')[0] == b'text/html':
# assumes ascii compatible encoding in body,
# but the middleware should actually parse the
# content type header and figure out the
# encoding when doing that.
body += ('<!-- Execution time: %.2fsec -->' %
(now - then)).encode('ascii')
break
return body, status, headers
then = time.time()
host = environ.get('HTTP_X_HOST')
if host is not None:
environ['HTTP_HOST'] = host
# use the apply_filter function that applies a given filter
# function for both async and sync responses.
return apply_filter(app, environ, filter_func)
return new_application
app = proxy_and_timing_support(app)
Details der Spezifikation
Das Anwendungs-Callable muss ein positionsargument akzeptieren. Zur Veranschaulichung haben wir es environ genannt, aber es ist nicht erforderlich, diesen Namen zu haben. Ein Server oder Gateway **muss** das Anwendungsobjekt mit einem positionsbezogenen (nicht schlüsselwortbezogenen) Argument aufrufen. (Z. B. durch Aufruf von body, status, headers = application(environ) wie oben gezeigt.)
Der environ-Parameter ist ein Dictionary-Objekt, das CGI-Umgebungsvariablen enthält. Dieses Objekt **muss** ein eingebautes Python-Dictionary sein (nicht eine Unterklasse, UserDict oder eine andere Dictionary-Emulation), und die Anwendung darf das Dictionary beliebig modifizieren. Das Dictionary muss auch bestimmte Web3-erforderliche Variablen enthalten (die in einem späteren Abschnitt beschrieben werden) und kann auch serverspezifische Erweiterungsvariablen enthalten, die nach einer später zu beschreibenden Konvention benannt sind.
Wenn das Anwendungsobjekt vom Server aufgerufen wird, muss es ein Tuple mit drei Elementen zurückgeben: status, headers und body, oder, falls vom asynchronen Server unterstützt, ein argumentloses Callable, das entweder None oder ein Tuple dieser drei Elemente zurückgibt.
Das status-Element ist ein Status in Bytes in der Form b'999 Nachricht hier'.
headers ist eine Python-Liste von (header_name, header_value)-Paaren, die den HTTP-Antwort-Header beschreiben. Die headers-Struktur muss eine literale Python-Liste sein; sie muss Two-Tuples liefern. Sowohl header_name als auch header_value müssen Byte-Werte sein.
Der body ist ein Iterable, das null oder mehr Byte-Instanzen liefert. Dies kann auf verschiedene Weise erreicht werden, z. B. durch Rückgabe einer Liste mit Byte-Instanzen als body, oder durch Rückgabe einer Generatorfunktion als body, die Byte-Instanzen liefert, oder indem body eine Instanz einer iterierbaren Klasse ist. Unabhängig davon, wie es erreicht wird, muss das Anwendungsobjekt immer ein body-Iterable liefern, das null oder mehr Byte-Instanzen liefert.
Der Server oder das Gateway muss die gelieferten Bytes unbuffered an den Client übertragen und die Übertragung jedes Bytesatzes abschließen, bevor ein weiterer angefordert wird. (Mit anderen Worten, Anwendungen **sollten** ihre eigene Pufferung durchführen. Siehe den Abschnitt Pufferung und Streaming unten für weitere Informationen darüber, wie die Ausgabe von Anwendungen behandelt werden muss.)
Der Server oder das Gateway sollte die gelieferten Bytes als binäre Byte-Sequenzen behandeln: Insbesondere sollte er sicherstellen, dass Zeilenumbrüche nicht verändert werden. Die Anwendung ist dafür verantwortlich, sicherzustellen, dass die zu schreibenden Zeichenketten in einem für den Client geeigneten Format vorliegen. (Der Server oder das Gateway **kann** HTTP-Transferkodierungen anwenden oder andere Transformationen durchführen, um HTTP-Funktionen wie Byte-Bereichsübertragung zu implementieren. Siehe Andere HTTP-Funktionen unten für weitere Details.)
Wenn das von der Anwendung zurückgegebene body-Iterable eine close()-Methode hat, **muss** der Server oder das Gateway diese Methode nach Abschluss der aktuellen Anfrage aufrufen, unabhängig davon, ob die Anfrage normal abgeschlossen wurde oder aufgrund eines Fehlers frühzeitig beendet wurde. Dies dient der Unterstützung der Ressourcenfreigabe durch die Anwendung und soll die Generatorunterstützung von PEP 325 und andere übliche Iterables mit close()-Methoden ergänzen.
Schließlich **dürfen** Server und Gateways keine anderen Attribute des von der Anwendung zurückgegebenen body-Iterables direkt verwenden.
environ-Variablen
Das environ-Dictionary muss verschiedene CGI-Umgebungsvariablen enthalten, wie in der Common Gateway Interface-Spezifikation [2] definiert.
Die folgenden CGI-Variablen **müssen** vorhanden sein. Jeder Schlüssel ist ein nativer String. Jeder Wert ist eine Byte-Instanz.
Hinweis
In Python 3.1+ ist ein „nativer String“ ein str-Typ, der mit dem Fehlerbehandler surrogateescape dekodiert wurde, wie es os.environ.__getitem__ tut. In Python 2.6 und 2.7 ist ein „nativer String“ ein str-Typ, der eine Byte-Sequenz darstellt.
REQUEST_METHOD- Die HTTP-Anfragemethode, wie z. B.
"GET"oder"POST". SCRIPT_NAME- Der anfängliche Teil des „Pfades“ der Anforderungs-URL, der dem Anwendungsobjekt entspricht, damit die Anwendung seinen virtuellen „Ort“ kennt. Dies kann die leere Byte-Instanz sein, wenn die Anwendung dem „Stammverzeichnis“ des Servers entspricht. SCRIPT_NAME ist eine Byte-Instanz, die eine Sequenz von URL-kodierten Segmenten darstellt, die durch das Schrägstrich-Zeichen (
/) getrennt sind. Es wird davon ausgegangen, dass%2F-Zeichen gemäß CGI in literale Schrägstrich-Zeichen innerhalb vonPATH_INFOdekodiert werden. PATH_INFO- Der Rest des „Pfades“ der Anforderungs-URL, der den virtuellen „Ort“ des Ziels der Anfrage innerhalb der Anwendung bezeichnet. Dies **kann** eine Byte-Instanz sein, wenn die Anforderungs-URL auf das Anwendungsstammverzeichnis zielt und keinen abschließenden Schrägstrich hat. PATH_INFO ist eine Byte-Instanz, die eine Sequenz von URL-kodierten Segmenten darstellt, die durch das Schrägstrich-Zeichen (
/) getrennt sind. Es wird davon ausgegangen, dass%2F-Zeichen gemäß CGI in literale Schrägstrich-Zeichen innerhalb vonPATH_INFOdekodiert werden. QUERY_STRING- Der Teil der Anforderungs-URL (in Bytes), der dem
"?"folgt, falls vorhanden, oder die leere Byte-Instanz. SERVER_NAME,SERVER_PORT- In Kombination mit
SCRIPT_NAMEundPATH_INFO(oder ihren Rohäquivalenten) können diese Variablen verwendet werden, um die URL zu vervollständigen. Beachten Sie jedoch, dassHTTP_HOST, falls vorhanden, gegenüberSERVER_NAMEfür die Rekonstruktion der Anforderungs-URL bevorzugt werden sollte. Siehe den Abschnitt URL-Rekonstruktion unten für weitere Details.SERVER_PORTsollte eine Byte-Instanz und keine Ganzzahl sein. SERVER_PROTOCOL- Die Version des Protokolls, das der Client zum Senden der Anfrage verwendet hat. Typischerweise ist dies etwas wie
"HTTP/1.0"oder"HTTP/1.1"und kann von der Anwendung verwendet werden, um zu bestimmen, wie HTTP-Anfrage-Header behandelt werden sollen. (Diese Variable sollte wahrscheinlichREQUEST_PROTOCOLgenannt werden, da sie das in der Anfrage verwendete Protokoll bezeichnet und nicht notwendigerweise das Protokoll ist, das in der Antwort des Servers verwendet wird. Aus Kompatibilitätsgründen mit CGI müssen wir jedoch den bestehenden Namen beibehalten.)
Die folgenden CGI-Werte **können** in der Web3-Umgebung vorhanden sein. Jeder Schlüssel ist ein nativer String. Jede Wert ist eine Byte-Instanz.
CONTENT_TYPE- Der Inhalt von Feldern vom Typ
Content-Typein der HTTP-Anfrage. CONTENT_LENGTH- Der Inhalt von Feldern vom Typ
Content-Lengthin der HTTP-Anfrage. HTTP_-Variablen- Variablen, die den vom Client bereitgestellten HTTP-Anfrage-Headern entsprechen (d. h. Variablen, deren Namen mit
"HTTP_"beginnen). Das Vorhandensein oder Fehlen dieser Variablen sollte dem Vorhandensein oder Fehlen des entsprechenden HTTP-Headers in der Anfrage entsprechen.
Ein Server oder Gateway **sollte** versuchen, so viele andere CGI-Variablen wie zutreffend bereitzustellen, wobei jeder eine Zeichenkette für seinen Schlüssel und eine Byte-Instanz für seinen Wert hat. Zusätzlich, wenn SSL verwendet wird, **sollte** der Server oder das Gateway auch so viele der Apache SSL-Umgebungsvariablen [4] wie zutreffend bereitstellen, wie z. B. HTTPS=on und SSL_PROTOCOL. Beachten Sie jedoch, dass eine Anwendung, die andere CGI-Variablen als die oben genannten verwendet, zwangsläufig nicht portierbar auf Webserver ist, die die relevanten Erweiterungen nicht unterstützen. (Zum Beispiel können Webserver, die keine Dateien veröffentlichen, kein aussagekräftiges DOCUMENT_ROOT oder PATH_TRANSLATED bereitstellen.)
Ein Web3-konformer Server oder Gateway **sollte** dokumentieren, welche Variablen er bereitstellt, zusammen mit ihren entsprechenden Definitionen. Anwendungen **sollten** auf die Anwesenheit jeder benötigten Variable prüfen und einen Ausweichplan haben, falls eine solche Variable fehlt.
Beachten Sie, dass CGI-Variablen-*Werte* Byte-Instanzen sein müssen, wenn sie überhaupt vorhanden sind. Es verstößt gegen diese Spezifikation, wenn der Wert einer CGI-Variable von einem anderen Typ als bytes ist. Unter Python 2 sind dies dann vom Typ str. Unter Python 3 sind dies dann vom Typ bytes.
Die *Schlüssel* aller CGI- und Nicht-CGI-Variablen im environ müssen jedoch „native Strings“ sein (sowohl unter Python 2 als auch unter Python 3 vom Typ str).
Zusätzlich zu den CGI-definierten Variablen **kann** das environ-Dictionary auch beliebige Betriebssystem-„Umgebungsvariablen“ enthalten und **muss** die folgenden Web3-definierten Variablen enthalten.
| Variable. | Wert |
|---|---|
web3.version |
Das Tuple (1, 0), das die Web3-Version 1.0 darstellt. |
web3.url_scheme |
Ein Byte-Wert, der den „scheme“-Teil der URL darstellt, unter der die Anwendung aufgerufen wird. Normalerweise hat er den Wert b"http" oder b"https", je nach Fall. |
web3.input |
Ein Eingabestream (dateiähnliches Objekt), von dem Bytes gelesen werden können, die den Body der HTTP-Anfrage bilden. (Der Server oder das Gateway kann Lesezugriffe nach Bedarf der Anwendung durchführen oder den Body der Client-Anfrage vorab lesen und im Speicher oder auf der Festplatte puffern oder jede andere Technik zur Bereitstellung eines solchen Eingabestroms nach Belieben verwenden.) |
web3.errors |
Ein Ausgabestrom (dateiähnliches Objekt), in den Fehlertext geschrieben werden kann, um Programm- oder andere Fehler an einem standardisierten und möglicherweise zentralisierten Ort aufzuzeichnen. Dies sollte ein „Textmodus“-Stream sein; d. h., Anwendungen sollten "\n" als Zeilenende verwenden und davon ausgehen, dass es vom Server/Gateway in das korrekte Zeilenende umgewandelt wird. Anwendungen dürfen keine Bytes an die ‚write‘-Methode dieses Streams senden; sie dürfen nur Text senden.Für viele Server ist |
web3.multithread |
Dieser Wert sollte wahr sein, wenn das Anwendungsobjekt gleichzeitig von einem anderen Thread im selben Prozess aufgerufen werden kann, und sollte andernfalls falsch sein. |
web3.multiprocess |
Dieser Wert sollte wahr sein, wenn ein gleichwertiges Anwendungsobjekt gleichzeitig von einem anderen Prozess aufgerufen werden kann, und sollte andernfalls falsch sein. |
web3.run_once |
Dieser Wert sollte wahr sein, wenn der Server oder das Gateway erwartet (aber nicht garantiert!), dass die Anwendung während der Lebensdauer ihres enthaltenden Prozesses nur einmal aufgerufen wird. Normalerweise ist dies nur bei einem Gateway der Fall, das auf CGI (oder etwas Ähnlichem) basiert. |
web3.script_name |
Der nicht URL-dekodierte Wert von SCRIPT_NAME. Aufgrund einer historischen Ungleichheit ist SCRIPT_NAME gemäß der CGI-Spezifikation in der Umgebung als bereits URL-dekodierte Zeichenkette vorhanden. Dies ist der ursprüngliche URL-kodierte Wert, der von der Anfrage-URI abgeleitet wurde. Wenn der Server diesen Wert nicht bereitstellen kann, muss er ihn aus der Umgebung weglassen. |
web3.path_info |
Der nicht URL-dekodierte Wert von PATH_INFO. Aufgrund einer historischen Ungleichheit ist PATH_INFO gemäß der CGI-Spezifikation in der Umgebung als bereits URL-dekodierte Zeichenkette vorhanden. Dies ist der ursprüngliche URL-kodierte Wert, der von der Anfrage-URI abgeleitet wurde. Wenn der Server diesen Wert nicht bereitstellen kann, muss er ihn aus der Umgebung weglassen. |
web3.async |
Dies ist True, wenn der Webserver asynchrone Aufrufe unterstützt. In diesem Fall darf eine Anwendung anstelle eines Tupels mit der Antwort ein Callable zurückgeben. Die genauen Semantiken sind in dieser Spezifikation nicht festgelegt. |
Schließlich **kann** das environ-Dictionary auch serverspezifische Variablen enthalten. Diese Variablen sollten Namen haben, die native Strings sind, nur Kleinbuchstaben, Zahlen, Punkte und Unterstriche enthalten und mit einem Namen präfigiert sein, der für den definierenden Server oder das Gateway eindeutig ist. Zum Beispiel könnte mod_web3 Variablen mit Namen wie mod_web3.some_variable definieren.
Eingabestream
Der vom Server bereitgestellte Eingabestrom (web3.input) muss die folgenden Methoden unterstützen
| Methode | Anmerkungen |
|---|---|
read(size) |
1,4 |
readline([size]) |
1,2,4 |
readlines([size]) |
1,3,4 |
__iter__() |
4 |
Die Semantik jeder Methode ist wie in der Python Library Reference dokumentiert, außer für diese Hinweise, die in der obigen Tabelle aufgeführt sind:
- Der Server ist nicht verpflichtet, über die vom Client angegebene
Content-Lengthhinaus zu lesen, und darf eine End-of-File-Bedingung simulieren, wenn die Anwendung versucht, über diesen Punkt hinaus zu lesen. Die Anwendung **sollte nicht** versuchen, mehr Daten zu lesen, als durch die VariableCONTENT_LENGTHangegeben ist. - Die Implementierung muss das optionale
size-Argument fürreadline()unterstützen. - Die Anwendung kann freiwillig kein
size-Argument anreadlines()übergeben, und der Server oder das Gateway kann den Wert eines übergebenensize-Arguments ignorieren. - Die Methoden
read,readlineund__iter__müssen eine Byte-Instanz zurückgeben. Die Methodereadlinesmuss eine Sequenz zurückgeben, die Byte-Instanzen enthält.
Die oben in der Tabelle aufgeführten Methoden **müssen** von allen dieser Spezifikation entsprechenden Servern unterstützt werden. Von dieser Spezifikation entsprechende Anwendungen **dürfen nicht** andere Methoden oder Attribute des input-Objekts verwenden. Insbesondere **dürfen** Anwendungen nicht versuchen, diesen Stream zu schließen, auch wenn er eine close()-Methode besitzt.
Der Eingabestrom sollte Versuche, mehr als die Inhaltslänge der Anfrage zu lesen, stillschweigend ignorieren. Wenn keine Inhaltslänge angegeben ist, muss der Stream ein Dummy-Stream sein, der nichts zurückgibt.
Fehlerstrom
Der vom Server bereitgestellte Fehlerstrom (web3.errors) muss die folgenden Methoden unterstützen
| Methode | Stream | Anmerkungen |
|---|---|---|
flush() |
errors |
1 |
write(str) |
errors |
2 |
writelines(seq) |
errors |
2 |
Die Semantik jeder Methode ist wie in der Python Library Reference dokumentiert, außer für diese Hinweise, die in der obigen Tabelle aufgeführt sind:
- Da der
errors-Stream nicht zurückgespult werden kann, sind Server und Gateways frei, Schreiboperationen sofort weiterzuleiten, ohne zu puffern. In diesem Fall kann dieflush()-Methode ein No-Op sein. Portierbare Anwendungen können jedoch nicht davon ausgehen, dass die Ausgabe unbuffered ist oder dassflush()ein No-Op ist. Sie müssenflush()aufrufen, wenn sie sicherstellen müssen, dass die Ausgabe tatsächlich geschrieben wurde. (Zum Beispiel, um die Vermischung von Daten aus mehreren Prozessen zu minimieren, die in dasselbe Fehlerprotokoll schreiben.) - Die Methode
write()muss ein String-Argument akzeptieren, muss aber nicht unbedingt ein Byte-Argument akzeptieren. Die Methodewritelines()muss ein Sequenzargument akzeptieren, das ausschließlich aus Strings besteht, muss aber nicht unbedingt Byte-Instanzen als Mitglieder der Sequenz akzeptieren.
Die oben in der Tabelle aufgeführten Methoden **müssen** von allen dieser Spezifikation entsprechenden Servern unterstützt werden. Von dieser Spezifikation entsprechende Anwendungen **dürfen nicht** andere Methoden oder Attribute des errors-Objekts verwenden. Insbesondere **dürfen** Anwendungen nicht versuchen, diesen Stream zu schließen, auch wenn er eine close()-Methode besitzt.
Von einer Web3-Anwendung zurückgegebene Werte
Web3-Anwendungen geben ein Tuple in der Form (status, headers, body) zurück. Wenn der Server asynchrone Anwendungen unterstützt (web3.async), kann die Antwort ein aufrufbares Objekt sein (das keine Argumente akzeptiert).
Der status-Wert wird von einem Gateway oder Server als HTTP-„Status“-Byte-Instanz angenommen, wie z. B. b'200 OK' oder b'404 Nicht gefunden'. Das heißt, es ist eine Zeichenkette, die aus einem Statuscode und einem Reason-Phrase besteht, in dieser Reihenfolge und durch ein einzelnes Leerzeichen getrennt, ohne umgebende Leerzeichen oder andere Zeichen. (Siehe RFC 2616, Abschnitt 6.1.1 für weitere Informationen.) Die Zeichenkette **darf keine** Steuerzeichen enthalten und darf nicht mit einem Wagenrücklauf, Zeilenvorschub oder einer Kombination davon enden.
Der headers-Wert wird von einem Gateway oder Server als literale Python-Liste von (header_name, header_value)-Tupeln angenommen. Jeder header_name muss eine Byte-Instanz sein, die einen gültigen HTTP-Headerfeldnamen darstellt (wie in RFC 2616, Abschnitt 4.2 definiert), ohne einen abschließenden Doppelpunkt oder andere Satzzeichen. Jeder header_value muss eine Byte-Instanz sein und **darf keine** Steuerzeichen, einschließlich Wagenrückläufe oder Zeilenumbrüche, weder eingebettet noch am Ende, enthalten. (Diese Anforderungen dienen dazu, die Komplexität der Parsing-Vorgänge zu minimieren, die von Servern, Gateways und Zwischenantwortprozessoren durchgeführt werden müssen, die Antwortheader einsehen oder ändern müssen.)
Im Allgemeinen ist der Server oder das Gateway dafür verantwortlich, sicherzustellen, dass korrekte Header an den Client gesendet werden: Wenn die Anwendung einen von HTTP (oder anderen geltenden Spezifikationen) geforderten Header weglässt, **muss** der Server oder das Gateway ihn hinzufügen. Zum Beispiel würden die HTTP-Header Date: und Server: normalerweise vom Server oder Gateway geliefert. Das Gateway darf jedoch keine Werte mit demselben Namen überschreiben, wenn sie von der Anwendung ausgegeben werden.
(Eine Erinnerung für Server/Gateway-Autoren: HTTP-Header-Namen sind nicht case-sensitiv, also stellen Sie sicher, dass Sie das bei der Überprüfung anwendungsseitig bereitgestellter Header berücksichtigen!)
Anwendungen und Middleware dürfen keine HTTP/1.1 "Hop-by-Hop"-Features oder -Header, keine äquivalenten Features in HTTP/1.0 oder Header verwenden, die die Persistenz der Client-Verbindung zum Webserver beeinträchtigen würden. Diese Features sind die alleinige Domäne des eigentlichen Webservers, und ein Server oder Gateway **sollte** es als fatalen Fehler betrachten, wenn eine Anwendung versucht, diese zu senden, und einen Fehler auslösen, wenn sie als Rückgabewerte einer Anwendung in der headers-Struktur geliefert werden. (Für weitere Details zu "Hop-by-Hop"-Features und -Headern siehe den Abschnitt Andere HTTP-Features unten.)
Umgang mit Kompatibilität über Python-Versionen hinweg
Die Erstellung von Web3-Code, der sowohl unter Python 2.6/2.7 als auch unter Python 3.1+ läuft, erfordert von Seiten des Entwicklers einiges an Sorgfalt. Im Allgemeinen geht die Web3-Spezifikation von einer gewissen Gleichwertigkeit zwischen dem Python 2 str-Typ und dem Python 3 bytes-Typ aus. Zum Beispiel sind unter Python 2 die Werte in der Web3 environ Instanzen vom Typ str; in Python 3 sind dies Instanzen vom Typ bytes. Der Python 3 bytes-Typ verfügt nicht über alle Methoden des Python 2 str-Typs, und einige Methoden, die er besitzt, verhalten sich anders als der Python 2 str-Typ. Um sicherzustellen, dass Web3-Middleware und -Anwendungen über Python-Versionen hinweg funktionieren, müssen Entwickler Folgendes tun:
- Gehen Sie nicht von Gleichwertigkeit bei Vergleichen zwischen Textwerten und Byte-Werten aus. Wenn Sie dies tun, funktioniert Ihr Code möglicherweise unter Python 2, aber nicht richtig unter Python 3. Schreiben Sie zum Beispiel nicht
somebytes == 'abc'. Dies ist unter Python 2 manchmal wahr, aber unter Python 3 niemals, da eine Byte-Sequenz unter Python 3 niemals gleich einem String ist. Vergleichen Sie stattdessen immer einen Byte-Wert mit einem Byte-Wert, z.B. „somebytes == b’abc’“. Code, der dies tut, ist mit Python 2.6, 2.7 und 3.1 kompatibel und verhält sich in beiden Versionen gleich. Dasbvor'abc'signalisiert Python 3, dass der Wert eine literale Byte-Instanz ist; unter Python 2 ist dies ein Placebo zur Abwärtskompatibilität. - Verwenden Sie nicht die
__contains__-Methode (direkt oder indirekt) von Elementen, die als Byte-ähnlich gedacht sind, ohne sicherzustellen, dass ihr Argument ebenfalls eine Byte-Instanz ist. Wenn Sie dies tun, funktioniert Ihr Code möglicherweise unter Python 2, aber nicht richtig unter Python 3. Zum Beispiel löst'abc' in somebytes'unter Python 3 einenTypeErroraus, gibt aber unter Python 2.6 und 2.7Truezurück. Allerdings funktioniertb'abc' in somebytesin beiden Versionen gleich. In Python 3.2 kann diese Einschränkung teilweise aufgehoben werden, da gemunkelt wird, dass Byte-Typen eine__mod__-Implementierung erhalten könnten. __getitem__sollte nicht verwendet werden.- Versuchen Sie nicht, die
format-Methode oder die__mod__-Methode von Byte-Instanzen (direkt oder indirekt) zu verwenden. In Python 2 unterstützt derstr-Typ, den wir äquivalent zu den Python 3bytesbehandeln, diese Methoden, aber tatsächliche Python 3bytes-Instanzen unterstützen diese Methoden nicht. Wenn Sie diese Methoden verwenden, funktioniert Ihr Code unter Python 2, aber nicht unter Python 3. - Versuchen Sie nicht, einen Byte-Wert mit einem String-Wert zu verketten. Dies funktioniert möglicherweise unter Python 2, aber nicht unter Python 3. Zum Beispiel funktioniert
'abc' + somebytesunter Python 2, führt aber zu einemTypeErrorunter Python 3. Stellen Sie stattdessen immer sicher, dass Sie zwei Elemente desselben Typs verketten, z.B.b'abc' + somebytes.
Web3 erwartet Byte-Werte an anderen Stellen, z.B. in allen von einer Anwendung zurückgegebenen Werten.
Kurz gesagt, um die Kompatibilität von Web3-Anwendungscode zwischen Python 2 und Python 3 sicherzustellen, behandeln Sie in Python 2 CGI- und Server-Variablenwerte in der Umgebung so, als hätten sie die Python 3 bytes-API, obwohl sie tatsächlich eine leistungsfähigere API haben. Gleiches gilt für alle String-ähnlichen Werte, die von einer Web3-Anwendung zurückgegeben werden.
Puffern und Streaming
Generell erzielen Anwendungen den besten Durchsatz, indem sie ihre (moderat großen) Ausgaben puffern und alles auf einmal senden. Dies ist ein gängiger Ansatz in bestehenden Frameworks: Die Ausgabe wird in einem StringIO- oder ähnlichen Objekt gepuffert und dann zusammen mit den Antwortheadern auf einmal übertragen.
Der entsprechende Ansatz in Web3 besteht darin, dass die Anwendung einfach ein einzelementiges body-Iterable (wie eine Liste) zurückgibt, das den Antwortkörper als einzelnen String enthält. Dies ist der empfohlene Ansatz für die überwiegende Mehrheit der Anwendungsfunktionen, die HTML-Seiten rendern, deren Text leicht in den Speicher passt.
Für große Dateien oder für spezialisierte Anwendungen von HTTP-Streaming (wie z. B. Multipart "Server Push") muss eine Anwendung jedoch möglicherweise die Ausgabe in kleineren Blöcken bereitstellen (z. B. um zu vermeiden, eine große Datei in den Speicher zu laden). Es kommt auch manchmal vor, dass die Erstellung eines Teils einer Antwort zeitaufwändig ist, aber es wäre nützlich, den Teil der Antwort, der davor liegt, vorab zu senden.
In diesen Fällen gibt die Anwendung normalerweise einen body-Iterator zurück (oft einen Generator-Iterator), der die Ausgabe blockweise produziert. Diese Blöcke können so aufgeteilt werden, dass sie mit Multipart-Grenzen übereinstimmen (für "Server Push") oder kurz vor zeitaufwendigen Aufgaben (wie dem Lesen eines weiteren Blocks einer Datei auf der Festplatte).
Web3-Server, Gateways und Middleware **dürfen** die Übertragung eines Blocks nicht verzögern; sie **müssen** entweder den gesamten Block an den Client übertragen oder garantieren, dass sie die Übertragung auch dann fortsetzen, wenn die Anwendung ihren nächsten Block produziert. Ein Server/Gateway oder eine Middleware kann diese Garantie auf eine von drei Arten bieten:
- Den gesamten Block an das Betriebssystem senden (und anfordern, dass alle Puffer des Betriebssystems geleert werden), bevor die Kontrolle an die Anwendung zurückgegeben wird, ODER
- Einen anderen Thread verwenden, um sicherzustellen, dass die Übertragung des Blocks fortgesetzt wird, während die Anwendung den nächsten Block generiert.
- (Nur Middleware) sendet den gesamten Block an sein übergeordnetes Gateway/seinen übergeordneten Server.
Durch die Bereitstellung dieser Garantie ermöglicht Web3 Anwendungen, sicherzustellen, dass die Übertragung nicht an einer beliebigen Stelle ihrer Ausgabedaten ins Stocken gerät. Dies ist entscheidend für die ordnungsgemäße Funktion z.B. von Multipart "Server Push" Streaming, bei dem Daten zwischen Multipart-Grenzen vollständig an den Client übertragen werden sollten.
Unicode-Probleme
HTTP unterstützt Unicode nicht direkt, und diese Schnittstelle auch nicht. Alle Kodierungs-/Dekodierungsvorgänge müssen von der **Anwendung** gehandhabt werden; alle an den Server übergebenen oder vom Server empfangenen Werte müssen vom Python 3-Typ bytes oder Instanzen des Python 2-Typs str sein, nicht Python 2 unicode- oder Python 3 str-Objekte.
Alle in dieser Spezifikation erwähnten „Byte-Instanzen“ **müssen**
- Unter Python 2 vom Typ
strsein. - Unter Python 3 vom Typ
bytessein.
Alle „Byte-Instanzen“ **dürfen nicht**
- Unter Python 2 vom Typ
unicodesein. - Unter Python 3 vom Typ
strsein.
Das Ergebnis der Verwendung eines textähnlichen Objekts, wo ein byteähnliches Objekt erforderlich ist, ist undefiniert.
Von einer Web3-App zurückgegebene Werte als Status oder als Antwort-Header **müssen** die RFC 2616 bezüglich der Kodierung befolgen. Das heißt, die zurückgegebenen Bytes müssen einen Zeichenstrom von ISO-8859-1-Zeichen enthalten, oder der Zeichenstrom sollte RFC 2047 MIME-Kodierung verwenden.
Auf Python-Plattformen, die keinen nativen byteähnlichen Typ haben (z.B. IronPython etc.), sondern stattdessen im Allgemeinen textähnliche Strings zur Darstellung von Byte-Daten verwenden, kann die Definition von „Byte-Instanz“ geändert werden: Ihre „Byte-Instanzen“ müssen native Strings sein, die nur Code-Punkte enthalten, die in der ISO-8859-1-Kodierung darstellbar sind (\u0000 bis \u00FF, einschließlich). Es ist ein fataler Fehler für eine Anwendung auf einer solchen Plattform, Strings mit anderen Unicode-Zeichen oder Code-Punkten zu liefern. Ebenso **dürfen** Server und Gateways auf diesen Plattformen keine Strings an eine Anwendung liefern, die andere Unicode-Zeichen enthalten.
HTTP 1.1 Expect/Continue
Server und Gateways, die HTTP 1.1 implementieren, müssen transparente Unterstützung für den "Expect/Continue"-Mechanismus von HTTP 1.1 bieten. Dies kann auf verschiedene Arten erfolgen:
- Auf Anfragen mit
Expect: 100-continuemit einer sofortigen "100 Continue"-Antwort reagieren und normal fortfahren. - Fahren Sie normal mit der Anfrage fort, aber stellen Sie der Anwendung einen
web3.input-Stream zur Verfügung, der die "100 Continue"-Antwort sendet, wenn/sobald die Anwendung zum ersten Mal versucht, aus dem Eingabestrom zu lesen. Die Leseanfrage muss dann blockiert bleiben, bis der Client antwortet. - Warten, bis der Client entscheidet, dass der Server Expect/Continue nicht unterstützt und den Anfrage-Body selbst sendet. (Dies ist suboptimal und nicht empfehlenswert.)
Beachten Sie, dass diese Verhaltensbeschränkungen nicht für HTTP 1.0-Anfragen gelten oder für Anfragen, die nicht an ein Anwendungsobjekt gerichtet sind. Weitere Informationen zu HTTP 1.1 Expect/Continue finden Sie in RFC 2616, Abschnitte 8.2.3 und 10.1.1.
Andere HTTP-Funktionen
Im Allgemeinen sollten Server und Gateways „dumm spielen“ und der Anwendung die vollständige Kontrolle über ihre Ausgabe überlassen. Sie sollten nur Änderungen vornehmen, die die effektiven Semantiken der Antwort der Anwendung nicht verändern. Es ist immer möglich, dass der Anwendungsentwickler Middleware-Komponenten hinzufügt, um zusätzliche Features bereitzustellen. Daher sollten Server-/Gateway-Entwickler bei ihrer Implementierung konservativ sein. In gewissem Sinne sollte sich ein Server wie ein HTTP "Gateway Server" betrachten, wobei die Anwendung ein HTTP "Origin Server" ist. (Siehe RFC 2616, Abschnitt 1.3, für die Definition dieser Begriffe.)
Da Web3-Server und -Anwendungen jedoch nicht über HTTP kommunizieren, gelten die von RFC 2616 als "Hop-by-Hop"-Header nicht für die interne Web3-Kommunikation. Web3-Anwendungen **dürfen keine** "Hop-by-Hop"-Header generieren, keine HTTP-Features verwenden, die die Generierung solcher Header erfordern würden, oder sich auf den Inhalt eingehender "Hop-by-Hop"-Header im environ-Dictionary verlassen. Web3-Server **müssen** unterstützte eingehende "Hop-by-Hop"-Header selbst verarbeiten, z.B. durch Dekodierung jeder eingehenden Transfer-Encoding, einschließlich der Chunked-Kodierung, falls zutreffend.
Wenn man diese Prinzipien auf eine Vielzahl von HTTP-Features anwendet, sollte klar sein, dass ein Server die Cache-Validierung über die If-None-Match und If-Modified-Since-Anfrageheader und die Last-Modified und ETag-Antwortheader handhaben kann. Dies ist jedoch nicht erforderlich, und die Anwendung sollte ihre eigene Cache-Validierung durchführen, wenn sie diese Funktion unterstützen möchte, da der Server/Gateway eine solche Validierung nicht durchführen muss.
Ähnlich kann ein Server die Antwort einer Anwendung neu kodieren oder transportkodieren, aber die Anwendung sollte selbst eine geeignete Inhaltskodierung verwenden und darf keine Transportkodierung anwenden. Ein Server kann Byte-Bereiche der Antwort der Anwendung übertragen, wenn diese vom Client angefordert werden und die Anwendung Byte-Bereiche nicht nativ unterstützt. Auch hier sollte die Anwendung diese Funktion jedoch selbst durchführen, wenn gewünscht.
Beachten Sie, dass diese Einschränkungen für Anwendungen nicht unbedingt bedeuten, dass jede Anwendung jede HTTP-Funktion neu implementieren muss; viele HTTP-Funktionen können teilweise oder vollständig von Middleware-Komponenten implementiert werden, wodurch sowohl Server- als auch Anwendungsautoren von der wiederholten Implementierung derselben Funktionen befreit werden.
Thread-Unterstützung
Die Thread-Unterstützung oder deren Fehlen ist ebenfalls serverabhängig. Server, die mehrere Anfragen parallel ausführen können, sollten auch die Option bieten, eine Anwendung im Single-Thread-Modus auszuführen, damit Anwendungen oder Frameworks, die nicht Thread-sicher sind, auch mit diesem Server verwendet werden können.
Implementierungs-/Anwendungsnotizen
Server-Erweiterungs-APIs
Einige Serverautoren möchten möglicherweise erweiterte APIs bereitstellen, die Anwendungs- oder Framework-Autoren für spezialisierte Zwecke nutzen können. Zum Beispiel könnte ein Gateway, das auf mod_python basiert, einen Teil der Apache-API als Web3-Erweiterung bereitstellen.
Im einfachsten Fall erfordert dies nichts weiter als die Definition einer environ-Variablen, z. B. mod_python.some_api. In vielen Fällen kann die mögliche Anwesenheit von Middleware dies jedoch erschweren. Beispielsweise könnte eine API, die Zugriff auf dieselben HTTP-Header bietet, wie sie in environ-Variablen zu finden sind, andere Daten zurückgeben, wenn environ von Middleware geändert wurde.
Im Allgemeinen laufen Erweiterungs-APIs, die einen Teil der Web3-Funktionalität duplizieren, ersetzen oder umgehen, Gefahr, mit Middleware-Komponenten inkompatibel zu sein. Server-/Gateway-Entwickler sollten *nicht* davon ausgehen, dass niemand Middleware verwendet, da einige Framework-Entwickler ihre Frameworks speziell so organisieren, dass sie fast ausschließlich als Middleware verschiedener Arten fungieren.
Um also maximale Kompatibilität zu gewährleisten, **müssen** Server und Gateways, die Erweiterungs-APIs bereitstellen, die einige Web3-Funktionalitäten ersetzen, diese APIs so gestalten, dass sie über den Teil der API aufgerufen werden, den sie ersetzen. Zum Beispiel muss eine Erweiterungs-API für den Zugriff auf HTTP-Anfrage-Header erfordern, dass die Anwendung ihr aktuelles environ übergibt, damit der Server/Gateway überprüfen kann, ob über die API zugängliche HTTP-Header nicht durch Middleware verändert wurden. Wenn die Erweiterungs-API nicht garantieren kann, dass sie immer mit environ über den Inhalt von HTTP-Headern übereinstimmt, muss sie den Dienst für die Anwendung verweigern, z.B. indem sie einen Fehler auslöst, None anstelle einer Header-Sammlung zurückgibt oder was auch immer für die API angemessen ist.
Diese Richtlinien gelten auch für Middleware, die Informationen wie geparste Cookies, Formulardaten, Sitzungen und dergleichen zu environ hinzufügt. Insbesondere sollte solche Middleware diese Funktionen als Funktionen bereitstellen, die auf environ operieren, anstatt Werte einfach in environ zu stopfen. Dies trägt dazu bei, dass Informationen aus environ berechnet werden, nachdem jede Middleware URL-Rewrites oder andere environ-Änderungen vorgenommen hat.
Es ist sehr wichtig, dass diese "sicheren Erweiterungsregeln" sowohl von Server-/Gateway- als auch von Middleware-Entwicklern befolgt werden, um eine Zukunft zu vermeiden, in der Middleware-Entwickler gezwungen sind, alle Erweiterungs-APIs aus environ zu löschen, um sicherzustellen, dass ihre Vermittlung nicht von Anwendungen umgangen wird, die diese Erweiterungen nutzen!
Anwendungskonfiguration
Diese Spezifikation definiert nicht, wie ein Server eine Anwendung auswählt oder erhält, um sie aufzurufen. Dies und andere Konfigurationsoptionen sind sehr serverspezifische Angelegenheiten. Es wird erwartet, dass Server-/Gateway-Autoren dokumentieren, wie der Server für die Ausführung eines bestimmten Anwendungsobjekts konfiguriert wird und mit welchen Optionen (wie Threading-Optionen).
Framework-Autoren hingegen sollten dokumentieren, wie ein Anwendungsobjekt erstellt wird, das die Funktionalität ihres Frameworks umschließt. Der Benutzer, der sowohl den Server als auch das Anwendungsframework gewählt hat, muss diese beiden miteinander verbinden. Da jedoch sowohl das Framework als auch der Server eine gemeinsame Schnittstelle haben, sollte dies lediglich eine mechanische Angelegenheit und keine bedeutende technische Anstrengung für jedes neue Server-/Framework-Paar sein.
Schließlich möchten einige Anwendungen, Frameworks und Middleware möglicherweise das environ-Dictionary verwenden, um einfache String-Konfigurationsoptionen zu empfangen. Server und Gateways sollten dies unterstützen, indem sie es dem Bereitsteller einer Anwendung ermöglichen, Namens-Wert-Paare anzugeben, die in environ eingefügt werden sollen. Im einfachsten Fall kann diese Unterstützung lediglich darin bestehen, alle vom Betriebssystem bereitgestellten Umgebungsvariablen aus os.environ in das environ-Dictionary zu kopieren, da der Bereitsteller diese prinzipiell extern zum Server konfigurieren kann oder im CGI-Fall über die Konfigurationsdateien des Servers festgelegt werden können.
Anwendungen sollten versuchen, solche erforderlichen Variablen auf ein Minimum zu beschränken, da nicht alle Server eine einfache Konfiguration dafür unterstützen werden. Selbst im schlimmsten Fall können Personen, die eine Anwendung bereitstellen, natürlich ein Skript erstellen, um die erforderlichen Konfigurationswerte bereitzustellen
from the_app import application
def new_app(environ):
environ['the_app.configval1'] = b'something'
return application(environ)
Die meisten bestehenden Anwendungen und Frameworks benötigen wahrscheinlich nur einen einzigen Konfigurationswert aus environ, um den Speicherort ihrer anwendungs- oder frameworkspezifischen Konfigurationsdatei(en) anzugeben. (Natürlich sollten Anwendungen diese Konfiguration cachen, um zu vermeiden, sie bei jedem Aufruf erneut lesen zu müssen.)
URL-Rekonstruktion
Wenn eine Anwendung eine vollständige URL einer Anfrage (als Byte-Objekt) rekonstruieren möchte, kann sie dies mit dem folgenden Algorithmus tun
host = environ.get('HTTP_HOST')
scheme = environ['web3.url_scheme']
port = environ['SERVER_PORT']
query = environ['QUERY_STRING']
url = scheme + b'://'
if host:
url += host
else:
url += environ['SERVER_NAME']
if scheme == b'https':
if port != b'443':
url += b':' + port
else:
if port != b'80':
url += b':' + port
if 'web3.script_name' in url:
url += url_quote(environ['web3.script_name'])
else:
url += environ['SCRIPT_NAME']
if 'web3.path_info' in environ:
url += url_quote(environ['web3.path_info'])
else:
url += environ['PATH_INFO']
if query:
url += b'?' + query
Beachten Sie, dass eine solche rekonstruierte URL möglicherweise nicht genau dieselbe URI ist, die vom Client angefordert wurde. Server-Rewrite-Regeln können beispielsweise die ursprünglich vom Client angeforderte URL geändert haben, um sie in eine kanonische Form zu bringen.
Offene Fragen
file_wrapper-Ersatz. Derzeit ist hier nichts spezifiziert, aber es ist klar, dass das alte System der In-Band-Signalisierung kaputt ist, wenn es keine Möglichkeit bietet, als Middleware im Prozess zu erkennen, ob die Antwort ein File-Wrapper ist.
Diskussionspunkte
Unten sind mögliche Streitpunkte bezüglich dieser Spezifikation aufgeführt.
WSGI 1.0 Kompatibilität
Komponenten, die mit der WSGI 1.0-Spezifikation geschrieben wurden, werden nicht transparent mit Komponenten interoperieren, die mit dieser Spezifikation geschrieben wurden. Das liegt daran, dass die Ziele dieses Vorschlags und die Ziele von WSGI 1.0 nicht direkt aufeinander abgestimmt sind.
WSGI 1.0 ist verpflichtet, Spezifikations-Level-Abwärtskompatibilität mit Python-Versionen zwischen 2.2 und 2.7 zu bieten. Diese Spezifikation verzichtet jedoch auf die Kompatibilität mit Python 2.5 und niedriger, um Kompatibilität zwischen relativ neuen Versionen von Python 2 (2.6 und 2.7) sowie relativ neuen Versionen von Python 3 (3.1) zu bieten.
Es ist derzeit unmöglich, Komponenten zu schreiben, die sowohl unter Python 2 als auch unter Python 3 zuverlässig funktionieren und die WSGI 1.0-Spezifikation verwenden, da die Spezifikation implizit davon ausgeht, dass CGI- und Server-Variablenwerte im Environ und über start_response zurückgegebene Werte eine Byte-Sequenz darstellen, auf die mit der Python 2-String-API zugegriffen werden kann. Sie geht davon aus, weil dieser Datentyp die sinnvolle Darstellung von Bytes in allen Python 2-Versionen war und WSGI 1.0 konzipiert wurde, bevor Python 3 existierte.
Der str-Typ von Python 3 unterstützt die vollständige API des Python 2 str-Typs, aber der str-Typ von Python 3 stellt keine Byte-Sequenz dar, sondern Text. Daher erfordert die Verwendung zur Darstellung von Environ-Werten auch, dass die Environ-Byte-Sequenz über eine bestimmte Kodierung in Text dekodiert wird. Wir können diese Bytes nicht in Text dekodieren (zumindest nicht auf eine Weise, bei der die Dekodierung eine andere Bedeutung hat als ein Tunnelmechanismus), ohne den Umfang von WSGI zu erweitern, um Server- und Gateway-Kenntnisse über Dekodierungsrichtlinien und -mechanismen einzubeziehen. WSGI 1.0 beschäftigte sich nie mit Kodierung und Dekodierung. Es machte Aussagen über zulässige Transportwerte und schlug vor, dass verschiedene Werte am besten mit einer bestimmten Kodierung oder einer anderen dekodiert werden könnten, aber es erforderte nie von einem Server, eine Dekodierung durchzuführen, bevor
Python 3 hat keinen String-ähnlichen Typ, der stattdessen zur Darstellung von Bytes verwendet werden kann: es hat einen bytes-Typ. Ein Byte-Typ verhält sich in Python 3.1+ ziemlich ähnlich wie ein Python 2 str, aber er verfügt nicht über ein Verhalten, das mit str.__mod__ gleichwertig ist, und sein Iterationsprotokoll, Containment, Sequenzbehandlung und Äquivalenzvergleiche sind unterschiedlich.
In beiden Fällen gibt es in Python 3 keinen Typ, der sich genau wie der Python 2 str-Typ verhält, und eine Möglichkeit, einen solchen Typ zu erstellen, existiert nicht, da es keine "String ABC" gibt, die den Bau eines geeigneten Typs ermöglichen würde. Aufgrund dieser Design-Inkompatibilität funktionieren bestehende WSGI 1.0-Server, Middleware und Anwendungen unter Python 3 nicht, auch nicht nach der Ausführung durch 2to3.
Bestehende Web-SIG-Diskussionen über die Aktualisierung der WSGI-Spezifikation, damit es möglich ist, eine WSGI-Anwendung zu schreiben, die sowohl unter Python 2 als auch unter Python 3 läuft, drehen sich tendenziell um die Schaffung einer Spezifikations-Äquivalenz zwischen dem Python 2 str-Typ (der eine Byte-Sequenz darstellt) und dem Python 3 str-Typ (der Text darstellt). Eine solche Äquivalenz wird in verschiedenen Bereichen angespannt, da diese Typen unterschiedliche Rollen haben. Eine wohl einfachere Äquivalenz besteht zwischen der API des Python 3 bytes-Typs und einer Teilmenge der API des Python 2 str-Typs. Diese Spezifikation nutzt diese Teilmengen-Äquivalenz aus.
In der Zwischenzeit, abgesehen von Kompatibilitätsproblemen zwischen Python 2 und Python 3, ist die WSGI 1.0-Spezifikation, wie verschiedene Diskussionen auf Web-SIG gezeigt haben, zu allgemein und unterstützt (über .write) asynchrone Anwendungen auf Kosten der Implementierungskomplexität. Diese Spezifikation nutzt die grundlegende Inkompatibilität zwischen WSGI 1.0 und Python 3 als natürlichen Divergenzpunkt, um eine Spezifikation mit reduzierter Komplexität durch Änderung der spezialisierten Unterstützung für asynchrone Anwendungen zu schaffen.
Um die Abwärtskompatibilität für ältere WSGI 1.0-Anwendungen zu gewährleisten, damit diese auf einem Web3-Stack laufen können, wird angenommen, dass Web3-Middleware erstellt wird, die "vor" bestehenden WSGI 1.0-Anwendungen verwendet werden kann, damit diese bestehenden WSGI 1.0-Anwendungen unter einem Web3-Stack laufen können. Diese Middleware wird unter Python 3 eine Äquivalenz zwischen Python 3 str-Typen und den Byte-Werten, die durch die HTTP-Anfrage repräsentiert werden, sowie aller damit verbundenen Kodierungsraterei (oder Konfiguration) erfordern.
Hinweis
Solche Middleware **könnte** zukünftig anstelle einer Äquivalenz zwischen Python 3 str und HTTP-Byte-Werten einen noch zu erstellenden "ebytes"-Typ (auch "Bytes mit Vorteilen" genannt) nutzen, insbesondere wenn ein String-ABC-Vorschlag in den Python-Kern aufgenommen und implementiert wird.
Umgekehrt wird angenommen, dass WSGI 1.0-Middleware erstellt wird, die es einer Web3-Anwendung ermöglicht, hinter einem WSGI 1.0-Stack auf der Python 2-Plattform zu laufen.
Environ und Response-Werte als Bytes
Gelegentliche Middleware- und Anwendungsentwickler mögen die Verwendung von Bytes als Umgebungs- und Antwortwerte als unpraktisch empfinden. Insbesondere können sie keine gängigen String-Formatierungsfunktionen wie ('%s' % bytes_val) oder bytes_val.format('123') verwenden, da Bytes auf Plattformen wie Python 3, wo sich die beiden Typen unterscheiden, nicht die gleiche API wie Strings haben. Ebenso ist auf solchen Plattformen die Unterstützung der stdlib HTTP-bezogenen APIs für die Verwendung von Bytes austauschbar mit Text oft lückenhaft. Wo Bytes unpraktisch oder mit Bibliotheks-APIs inkompatibel sind, müssen Middleware- und Anwendungsentwickler diese Bytes explizit in Text dekodieren. Dies ist besonders unpraktisch für Middleware-Entwickler: Um mit Umgebungs-Werten als Strings zu arbeiten, müssen sie diese aus einer impliziten Kodierung dekodieren, und wenn sie einen Environ-Wert ändern müssen, müssen sie den Wert dann in einen Byte-Stream kodieren, bevor sie ihn in das Environ einfügen. Obwohl die Verwendung von Bytes durch die Spezifikation als Environ-Werte für gelegentliche Entwickler unpraktisch sein mag, bietet sie mehrere Vorteile.
Die Verwendung von Byte-Typen zur Darstellung von HTTP- und Serverwerten für eine Anwendung entspricht am ehesten der Realität, da HTTP im Grunde ein Byte-orientiertes Protokoll ist. Wenn die Environ-Werte als Strings vorgeschrieben werden, muss jeder Server Heuristiken verwenden, um über die Kodierung verschiedener vom HTTP-Environ bereitgestellter Werte zu raten. Die Verwendung von ausschließlich Strings könnte die Bequemlichkeit von gelegentlichen Middleware-Entwicklern erhöhen, führt aber auch zu Mehrdeutigkeit und Verwirrung, wenn ein Wert nicht in einen aussagekräftigen Nicht-Surrogat-String dekodiert werden kann.
Die Verwendung von Bytes als Environ-Werte vermeidet die Notwendigkeit, dass die Spezifikation einen teilnehmenden Server über Kodierungskonfigurationsparameter informiert. Wenn Environ-Werte als Strings behandelt und somit aus Bytes dekodiert werden müssen, können Konfigurationsparameter schließlich notwendig werden, da Policy-Hinweise vom Anwendungsdeployer stammen. Eine solche Policy würde verwendet, um eine geeignete Dekodierungsstrategie unter verschiedenen Umständen zu erraten, wodurch die Last der Durchsetzung einer bestimmten Anwendungs-Kodierungspolicy auf den Server gelegt wird. Wenn der Server mehr als eine Anwendung bedienen muss, wird eine solche Konfiguration schnell komplex. Viele Policies wären auch nicht deklarativ ausdrückbar.
In Wirklichkeit ist HTTP ein kompliziertes und von Legacy geprägtes Protokoll, das einen komplexen Satz von Heuristiken erfordert, um Sinn zu ergeben. Es wäre schön, wenn wir dieses Protokoll zulassen könnten, uns vor dieser Komplexität zu schützen, aber das können wir nicht zuverlässig tun, während wir Anwendungsentwicklern ein Maß an Kontrolle bieten, das der Realität entspricht. Python-Anwendungen müssen oft mit Daten umgehen, die in der Umgebung eingebettet sind und nicht nur durch Legacy-Heuristiken analysiert werden müssen, sondern **nicht einmal einer bestehenden HTTP-Spezifikation entsprechen**. Obwohl diese Eventualitäten unangenehm sind, treten sie regelmäßig auf, was es unmöglich und unerwünscht macht, sie vor Anwendungsentwicklern zu verstecken, da Anwendungsentwickler die einzigen sind, die entscheiden können, wie angemessen auf eine Verletzung der HTTP-Spezifikation reagiert wird, wenn sie erkannt wird.
Einige haben für eine gemischte Verwendung von Byte- und String-Werten als Environ-*Werte* plädiert. Dieser Vorschlag vermeidet diese Strategie. Die ausschließliche Verwendung von Bytes als Environ-Werte ermöglicht es, diese Spezifikation vollständig im Kopf zu behalten; Sie müssen nicht raten, welche Werte Strings und welche Bytes sind.
Dieses Protokoll würde auch in den Kopf eines Entwicklers passen, wenn alle Environ-Werte Strings wären, aber diese Spezifikation verwendet diese Strategie nicht. Dies wird wahrscheinlich der Punkt der größten Auseinandersetzung bezüglich der Verwendung von Bytes sein. Zur Verteidigung von Bytes: Entwickler bevorzugen oft Protokolle mit konsistenten Verträgen, auch wenn die Verträge selbst suboptimal sind. Wenn wir Kodierungsprobleme vor einem Entwickler verbergen, bis ein Wert mit Surrogaten Probleme verursacht, nachdem er bereits über die I/O-Grenze seiner Anwendung hinausgegangen ist, muss er viel mehr Arbeit leisten, um Annahmen seiner Anwendung zu korrigieren, als wenn wir das Problem viel früher in Form von "hier sind einige Bytes, Sie dekodieren sie" präsentieren würden. Dies ist auch ein Gegenargument zur Annahme "Bytes sind unpraktisch": Während die Präsentation von Bytes für einen gelegentlichen Anwendungsentwickler, dem Randfälle egal sind, unpraktisch sein mag, sind sie für den Anwendungsentwickler, der mit komplexen, schmutzigen Eventualitäten umgehen muss, äußerst praktisch, da die Verwendung von Bytes ihm das angemessene Maß an Kontrolle mit einer klaren Verantwortungsabgrenzung ermöglicht.
Wenn das Protokoll Bytes verwendet, wird angenommen, dass Bibliotheken erstellt werden, die die Arbeit mit reinen Bytes im Environ und in Rückgabewerten angenehmer machen; zum Beispiel Analogien zu den WSGI 1.0-Bibliotheken namens "WebOb" und "Werkzeug". Solche Bibliotheken füllen die Lücke zwischen Bequemlichkeit und Kontrolle und ermöglichen es der Spezifikation, einfach und regelmäßig zu bleiben, während sie gelegentlichen Autoren dennoch eine bequeme Möglichkeit bieten, Web3-Middleware und Anwendungskomponenten zu erstellen. Dies scheint eine vernünftige Alternative zur Einbettung von Kodierungspolicies in das Protokoll zu sein, da viele solcher Bibliotheken unabhängig vom Protokoll erstellt werden können und Anwendungsentwickler diejenige auswählen können, die ihnen die entsprechenden Grade an Kontrolle und Bequemlichkeit für eine bestimmte Aufgabe bietet.
Hier sind einige Alternativen zur ausschließlichen Verwendung von Bytes
- Der Server soll alle Werte, die CGI- und Server-Environ-Werte darstellen, mit der
latin-1-Kodierung in Strings dekodieren, was verlustfrei ist. Schmuggle alle nicht dekodierbaren Bytes innerhalb des resultierenden Strings. - Kodieren Sie alle CGI- und Server-Environ-Werte mit der
utf-8-Kodierung mit demsurrogateescape-Fehlerbehandler in Strings. Dies funktioniert unter keinem vorhandenen Python 2. - Kodieren Sie einige Werte in Bytes und andere Werte in Strings, je nach ihrem typischen Verwendungszweck.
Anwendungen sollten web3.input über CONTENT_LENGTH hinaus lesen dürfen
Bei [5] behauptet Graham Dumpleton, dass wsgi.input den leeren String als Zeichen für das Ende der Daten zurückgeben muss und dass Anwendungen über die in CONTENT_LENGTH angegebene Byte-Anzahl hinaus lesen dürfen, indem sie sich nur auf den leeren String als EOF-Markierung verlassen. WSGI setzt voraus, dass eine Anwendung "sich gut verhält und nach dem Lesen aller durch CONTENT_LENGTH angegebenen Daten die Daten verarbeitet und jede Antwort zurückgibt. Dieselbe Socket-Verbindung könnte dann für eine nachfolgende Anfrage verwendet werden." Graham möchte, dass WSGI-Adapter rohe Socket-Verbindungen umschließen müssen: "dieses Wrapper-Objekt muss zählen, wie viele Daten gelesen wurden, und wenn die Datenmenge die gemäß CONTENT_LENGTH definierte Menge erreicht, sollten alle nachfolgenden Lesevorgänge einen leeren String zurückgeben." Dies kann nützlich sein, um Chunked Encoding und Eingabefilter zu unterstützen.
web3.input Unbekannte Länge
Es gibt keine dokumentierte Möglichkeit anzuzeigen, dass sich Inhalt in environ['web3.input'] befindet, aber die Inhaltslänge unbekannt ist.
read() von web3.input sollte No-Size Calling Convention unterstützen
Bei [5] behauptet Graham Dumpleton, dass die read()-Methode von wsgi.input ohne Argumente aufgerufen werden können sollte und dass das Ergebnis "aller verfügbarer Request-Inhalt" sein sollte. Diskussionsbedarf.
Kommentar von Armin: Ich habe die Spezifikation geändert, um dies von einer Implementierung zu verlangen. Ich hatte damit schon zu viele Probleme. Offen für Diskussionen.
Input-Filter sollten CONTENT_LENGTH in environ auf -1 setzen
Bei [5] schlägt Graham Dumpleton vor, dass ein Eingabefilter environ['CONTENT_LENGTH'] auf -1 setzen könnte, um anzuzeigen, dass er die Eingabe mutiert hat.
headers als literale Liste von Two-Tuples
Warum machen wir Anwendungen eine headers-Struktur zurückgeben, die eine literale Liste von Zweier-Tupeln ist? Ich denke, die Iterierbarkeit von headers muss beibehalten werden, während sie den Stack hinaufgeht, aber ich glaube nicht, dass wir sie jederzeit im laufenden Betrieb verändern können müssen. Könnten wir diese Anforderung lockern?
Kommentar Armin: Starkes Ja
Entfernte Anforderung, dass Middleware nicht blockieren darf
Diese Anforderung wurde entfernt: „Middleware-Komponenten dürfen die Iteration nicht blockieren und auf mehrere Werte von einem Anwendung-Iterable warten. Wenn die Middleware mehr Daten von der Anwendung ansammeln muss, bevor sie eine Ausgabe erzeugen kann, muss sie einen leeren String zurückgeben.“ Diese Anforderung existierte, um asynchrone Anwendungen und Server zu unterstützen (siehe PEP 333 „Middleware Handling of Block Boundaries“). Asynchrone Anwendungen werden nun explizit durch web3.async fähige Protokolle bedient (ein Web3-Anwendungsaufruf kann selbst einen Aufruf zurückgeben).
web3.script_name und web3.path_info
Diese Werte müssen gemäß dieser Spezifikation von einem Ursprungsserver in die Umgebung platziert werden. Im Gegensatz zu SCRIPT_NAME und PATH_INFO müssen dies die ursprünglichen, *URL-kodierten* Varianten sein, die aus der Anfrage-URI abgeleitet werden. Wir müssen wahrscheinlich herausfinden, wie diese ursprünglich berechnet werden sollen und welche Werte sie haben sollten, wenn der Server eine URL-Neuschreibung durchführt.
Lange Response-Header
Bob Brewer merkt im Web-SIG an [6]
Jeder header_value darf keine Steuerzeichen enthalten, einschließlich Wagenrückläufen oder Zeilenumbrüchen, weder eingebettet noch am Ende. (Diese Anforderungen sollen die Komplexität von Parsings minimieren, die von Servern, Gateways und zwischengeschalteten Antwortprozessoren durchgeführt werden müssen, die Antwort-Header inspizieren oder ändern müssen.) (PEP 333)
Das ist verständlich, aber HTTP-Header sind als (hauptsächlich) *TEXT definiert, und „Wörter aus *TEXT dürfen Zeichen aus anderen Zeichensätzen als ISO-8859-1 nur enthalten, wenn sie gemäß den Regeln von RFC 2047 kodiert sind.“ [2] Und RFC 2047 legt fest, dass „ein ‚encoded-word‘ nicht länger als 75 Zeichen sein darf… Wenn es wünschenswert ist, mehr Text zu kodieren, als in ein ‚encoded-word‘ von 75 Zeichen passt, können mehrere ‚encoded-word‘s (getrennt durch CRLF SPACE) verwendet werden.“ [3] Dies erfüllt auch die HTTP-Header-Faltungsregeln: „Header-Felder können über mehrere Zeilen erweitert werden, indem jede zusätzliche Zeile mit mindestens einem SP oder HT vorangestellt wird.“ (PEP 333)
Also, meiner Lesart von HTTP zufolge sollte irgendwo Code neue Zeilen in längeren, kodierten Antwort-Header-Werten einführen. Ich sehe drei Optionen:
- Die Dinge so belassen, wie sie sind, und Antwort-Header-Werte verbieten, wenn sie Wörter mit mehr als 75 Zeichen enthalten, die außerhalb des ISO-8859-1-Zeichensatzes liegen.
- Zeilenumbruchzeichen in WSGI-Antwort-Headern zulassen.
- WSGI-Server dazu verpflichten/stark empfehlen, die Kodierung und Faltung durchzuführen, bevor der Wert über HTTP gesendet wird.
Request-Trailer und Chunked Transfer Encoding
Bei Verwendung von „chunked transfer encoding“ für den Anforderungsinhalt erlauben die RFCs Anforderungstrailer. Diese sind wie Anforderungs-Header, kommen aber nach dem finalen Null-Daten-Chunk. Diese Trailer sind nur verfügbar, wenn der „chunked data stream“ eine endliche Länge hat und wenn er vollständig gelesen wurde. Weder WSGI noch Web3 unterstützen sie derzeit.
Referenzen
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0444.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT