Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wsgi support #17

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions twirp/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
import json

from . import base
from . import exceptions
from . import errors
from . import ctxkeys
from . import errors
from . import exceptions



try:
import contextvars # Python 3.7+ only.
Expand Down Expand Up @@ -57,7 +59,6 @@ async def __call__(self, scope, receive, send):
self._hook.request_received(ctx=ctx)

endpoint = self._get_endpoint(scope['path'])
headers = {k.decode('utf-8'): v.decode('utf-8') for (k,v) in scope['headers']}
encoder, decoder = self._get_encoder_decoder(endpoint, headers)
# add headers from request into context

Expand Down
95 changes: 95 additions & 0 deletions twirp/wsgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
from google.protobuf import json_format
from werkzeug.wrappers import Request, Response

from . import base
from . import ctxkeys
from . import exceptions
from . import errors


class TwirpWSGIApp(base.TwirpBaseApp):

def __call__(self, environ, start_response):
ctx = self._ctx_class()
try:
return self.handle_request(ctx, environ, start_response)
except Exception as e:

return self.handle_error(ctx, e, environ, start_response)

def handle_request(self, ctx, environ, start_response):
request = Request(environ)
ctx["request"] = request
self._hook.request_received(ctx=ctx)

http_method = request.method
if http_method != "POST":
raise exceptions.TwirpServerException(
code=errors.Errors.BadRoute,
message="unsupported method " + http_method + " (only POST is allowed)",
meta={"twirp_invalid_route": http_method + " " + scope['path']},
)
ctx["http_method"] = "POST"
ctx["url"] = request.path
ctx["content-type"] = request.headers["Content-Type"]

endpoint, func, decode, encode = self.get_endpoint_methods(request)
ctx["endpoint"] = endpoint
request_routed.send(ctx)

input_arg = decode(request)
ctx["input"] = input_arg
result = func(input_arg, ctx=ctx)
ctx["output"] = result
response = encode(result)
ctx["response"] = response
response_prepared.send(ctx)

ctx["status_code"] = 200
response_sent.send(ctx)

return response(environ, start_response)

def handle_error(self, ctx, exc, environ, start_response):
base_err = {
"type": "Internal",
"msg": ("There was an error but it could not be "
"serialized into JSON"),
"meta": {}
}
response = Response()
response.status_code = 500

try:
err = base_err
if isinstance(exc, TwirpServerException):
err["code"] = exc.code.value
err["msg"] = exc.message
if exc.meta:
for k, v in exc.meta.items():
err["meta"][k] = str(v)
response.status_code = Errors.get_status_code(exc.code)
else:
err["msg"] = "Internal non-Twirp Error"
err["code"] = 500
err["meta"] = {"raw_error": str(exc)}

for k, v in ctx.items():
err["meta"][k] = str(v)

response.set_data(json.dumps(err))
except Exception as e:
err = base_err
err["meta"] = {"original_error": str(exc),
"handling_error": str(e)}
response.set_data(json.dumps(err))

# Force json for errors.
response.headers["Content-Type"] = "application/json"

ctx["status_code"] = response.status_code
ctx["response"] = response
ctx["exception"] = exc
error_occurred.send(ctx)

return response(environ, start_response)