From 392af9784d6a94403e58d2c02652aedc3379bf4c Mon Sep 17 00:00:00 2001 From: Ciro Date: Wed, 4 Jan 2023 09:11:30 -0300 Subject: [PATCH] fix WSGI post error #64 --- bench/asgi_wsgi/flask-wsgi.py | 11 +++- pyproject.toml | 2 +- setup.py | 2 +- src/socketify/wsgi.py | 97 ++++++++++++++++++++++++++++++++++- 4 files changed, 107 insertions(+), 5 deletions(-) diff --git a/bench/asgi_wsgi/flask-wsgi.py b/bench/asgi_wsgi/flask-wsgi.py index c9f0e8c..0f91f8e 100644 --- a/bench/asgi_wsgi/flask-wsgi.py +++ b/bench/asgi_wsgi/flask-wsgi.py @@ -1,4 +1,4 @@ -from flask import Flask, make_response +from flask import Flask, make_response, request from socketify import WSGI app = Flask(__name__) @@ -10,6 +10,15 @@ def index(): response.content_type = "text/plain" return response +@app.route('/post', methods=['POST']) +def post_test(): + """Test 6: Plaintext""" + user = request.form["name"] + response = make_response(f"Hello, World {user}!") + response.content_type = "text/plain" + return response + + if __name__ == "__main__": WSGI(app).listen(8000, lambda config: print(f"Listening on port http://localhost:{config.port} now\n")).run() diff --git a/pyproject.toml b/pyproject.toml index d98ae29..e986ede 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "socketify" -version = "0.0.1" +version = "0.0.2" authors = [ { name="Ciro Spaciari", email="ciro.spaciari@gmail.com" }, ] diff --git a/setup.py b/setup.py index c64eb7a..c25b5e5 100644 --- a/setup.py +++ b/setup.py @@ -58,7 +58,7 @@ with open("README.md", "r", encoding="utf-8") as fh: setuptools.setup( name="socketify", - version="0.0.1", + version="0.0.2", platforms=["any"], author="Ciro Spaciari", author_email="ciro.spaciari@gmail.com", diff --git a/src/socketify/wsgi.py b/src/socketify/wsgi.py index c0f82d2..f9d4d56 100644 --- a/src/socketify/wsgi.py +++ b/src/socketify/wsgi.py @@ -2,7 +2,7 @@ import os import inspect from socketify import App from .asgi import ws_close, ws_upgrade, ws_open, ws_message -from io import BytesIO +from io import BytesIO, BufferedReader from .native import lib, ffi import platform is_pypy = platform.python_implementation() == "PyPy" @@ -22,6 +22,92 @@ def wsgi_on_data_handler(res, chunk, chunk_length, is_end, user_data): data_response._ptr, ) +class WSGIBody: + def __init__(self, buffer): + self.buf = buffer + self.reader = BufferedReader(buffer) + + def __iter__(self): + return self + + def __next__(self): + ret = self.readline() + if not ret: + raise StopIteration() + return ret + + next = __next__ + + def getsize(self, size): + if size is None: + return sys.maxsize + elif not isinstance(size, int): + raise TypeError("size must be an integral type") + elif size < 0: + return sys.maxsize + return size + + def read(self, size=None): + size = self.getsize(size) + if size == 0: + return b"" + + if size < self.buf.tell(): + data = self.buf.getvalue() + ret, rest = data[:size], data[size:] + self.buf = BytesIO() + self.buf.write(rest) + return ret + + while size > self.buf.tell(): + data = self.reader.read(1024) + if not data: + break + self.buf.write(data) + + data = self.buf.getvalue() + ret, rest = data[:size], data[size:] + self.buf = BytesIO() + self.buf.write(rest) + return ret + + def readline(self, size=None): + size = self.getsize(size) + if size == 0: + return b"" + + data = self.buf.getvalue() + self.buf = BytesIO() + + ret = [] + while 1: + idx = data.find(b"\n", 0, size) + idx = idx + 1 if idx >= 0 else size if len(data) >= size else 0 + if idx: + ret.append(data[:idx]) + self.buf.write(data[idx:]) + break + + ret.append(data) + size -= len(data) + data = self.reader.read(min(1024, size)) + if not data: + break + + return b"".join(ret) + + def readlines(self, size=None): + ret = [] + data = self.read() + while data: + pos = data.find(b"\n") + if pos < 0: + ret.append(data) + data = b"" + else: + line, data = data[:pos + 1], data[pos + 1:] + ret.append(line) + return ret class WSGIDataResponse: def __init__(self, app, environ, start_response, aborted, buffer, on_data): @@ -66,6 +152,9 @@ def wsgi(ssl, response, info, user_data, aborted): environ[f"HTTP_{name.replace('-', '_').upper()}"] = value next_header = ffi.cast("socketify_header*", next_header.next) + environ["CONTENT_TYPE"] = environ.get("HTTP_CONTENT_TYPE", None) + environ["CONTENT_LENGTH"] = environ.get("HTTP_CONTENT_LENGTH", "0") + def start_response(status, headers): if isinstance(status, str): data = status.encode("utf-8") @@ -120,13 +209,14 @@ def wsgi(ssl, response, info, user_data, aborted): # check for body if bool(info.has_content): WSGI_INPUT = BytesIO() - environ["wsgi.input"] = WSGI_INPUT + environ["wsgi.input"] = WSGIBody(WSGI_INPUT) def on_data(data_response, response): if bool(data_response.aborted[0]): return ssl = data_response.app.server.SSL + app_iter = data_response.app.wsgi( data_response.environ, data_response.start_response ) @@ -295,6 +385,9 @@ class _WSGI: "SCRIPT_NAME": "", "SERVER_PROTOCOL": "HTTP/1.1", "REMOTE_HOST": "", + "CONTENT_LENGTH": "0", + "CONTENT_TYPE": "0", + 'wsgi.input_terminated': True } ) self.server.listen(port_or_options, handler)