GithubHelp home page GithubHelp logo

awsgi's Introduction

Archived - Please use a fork or alternative

(See Pull Requests and Issues for suggestions)

AWSGI

A WSGI adapter for AWS API Gateway/Lambda Proxy Integration

AWSGI allows you to use WSGI-compatible middleware and frameworks like Flask and Django with the AWS API Gateway/Lambda proxy integration.

Installation

awsgi is available from PyPI as aws-wsgi:

pip install aws-wsgi

Example

import awsgi
from flask import (
    Flask,
    jsonify,
)

app = Flask(__name__)


@app.route('/')
def index():
    return jsonify(status=200, message='OK')


def lambda_handler(event, context):
    return awsgi.response(app, event, context, base64_content_types={"image/png"})

awsgi's People

Contributors

adamchainz avatar astraluma avatar chadawagner avatar frob avatar gabrielfalcao avatar harpsichord1207 avatar headljdghub avatar jamesrwhite avatar jstlaurent avatar mvanbaak avatar mwedgwood-rmn avatar slank avatar steadbytes avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

awsgi's Issues

Is there any way to download a file from s3?

I want to implements a download feature, when user request to download, lambda will get file content from s3, then response to api gateway.
Note: I want to download a binary file, eg: xxx.bin

Here is the code:

    file = s3.get_file_content(bucket_name, key)
    
    response = make_response(file['Body'].read())
    response.headers.set('Content-Type', 'application/octet-stream')
    response.headers.set(
        'Content-Disposition', 'attachment', filename='%s' % key)
    return response

But i got some error:
image

headers not treated as case-insensitive (Content-Type)

In "use_binary_response", the header 'Content-Type' is read; however, HTTP headers are case insensitive (it fails in my case).

Can be fixed with something like this:

        content_type = None
        for item in headers.items():
            if item[0].lower() == 'content-type':
                content_type = item[1]
                break

Request fail on parsing json body with accent character

I am calling an endpoint i made to update a customer. When passing my json body i get a 400 Bad request. Seems like a json body that contains accent characters is not parsed correctly by the request object.

Note: when using flask without awsgi like so:

@app.route('/', methods=['POST'])
def test():
    return request.get_json()

everything is working, but when i use awsgi:

def lambda_handler(event, context):
    return awsgi.response(app, event, context)

@bp.route('/<id>', methods=[METHOD])
def update_customer(id, query_params={}):
    return request.get_json()

This throw me a BadRequest error and the error is on get_json.

I don't really know what awsgi is doing with the json body, but something is done incorrectly.

Environment

  • Python version: 3.7.4
  • Flask version: 1.1.1
  • Werkzeug version: 0.16.0

Django example

It would we nice, if we have a django example just like the flask example in documentaion

new select_impl breaks if requestContext not present

I'm not sure whether requestContext is always provided from lambda event, but we have a use case where we're creating the event dict ourselves which did not contain a requestContext. New code to support elb tries to access event['requestContext'] which fails in that case. Would be easy enough to use .get(), I'll submit a PR.

Error with sending .zip file with send_file

Hi,
I want to create a dummy .zip file and send the response with the function send_file.

    memory_file = BytesIO()
    with zipfile.ZipFile(memory_file, 'w') as zf:
        zipinfo = zipfile.ZipInfo("test")
        zipinfo.date_time = time.localtime(time.time())[:6]
        zipinfo.compress_type = zipfile.ZIP_DEFLATED
        zf.writestr(zipinfo, "this is test zip content")
    memory_file.seek(0)
    return send_file(memory_file, attachment_filename='capsule.zip', as_attachment=True)

To see if the .zip file is processed and sent correctly I send the GET request via my browser.
The .zip file is sent, i download it but when I want to see the archive of the .zip I cannot because the .zip file is corrupted.

I updated the line 41 in the init.py file

    self.base64_content_types = set(base64_content_types or ["application/zip"]) or set()

I also tried doing the same thing without using AWSGI (using only python FlaskRESTful) and i had no issues.

Could you help me resolving this issue
Thanks

Support Multi Value Headers

Elastic Load Balancing supports Multi Value Headers under different keys

{
    "requestContext": {
        "elb": {
            "targetGroupArn": "arn:aws:elasticloadbalancing:us-east-2:0123456789:targetgroup/spam/eggs"
        }
    },
    "httpMethod": "GET",
    "path": "/",
    "multiValueQueryStringParameters": {},
    "multiValueHeaders": {
        "user-agent": [
            "ELB-HealthChecker/2.0"
        ]
    },
    "body": "",
    "isBase64Encoded": false
}

Crash after upgrade to 0.2.6 when no Content-Type header is set in response

After upgrading to version 0.2.6, I have the following issue on responses without a "Content-Type" header set:

[ERROR] TypeError: argument of type 'NoneType' is not iterable Traceback (most recent call last): File "/var/task/src/main/handler.py", line 262, in flask_handler return awsgi.response(app, event, context) File "dependencies/awsgi/__init__.py", line 172, in response return sr.response(output) File "dependencies/awsgi/__init__.py", line 94, in response rv = super(StartResponse_GW, self).response(output) File "dependencies/awsgi/__init__.py", line 88, in response rv.update(self.build_body(headers, output)) File "dependencies/awsgi/__init__.py", line 69, in build_body is_b64 = self.use_binary_response(headers, totalbody) File "dependencies/awsgi/__init__.py", line 60, in use_binary_response if ';' in content_type:

My case is an empty response with a 304 status code, so there is no content. The culprit seems to be this commit:

00c375c

>>> ';' in None Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: argument of type 'NoneType' is not iterable

A fix would be to check for None

No headers raises a KeyError

When the headers key has a value of None, a NoneType error is thrown on line 129 of init.py. Here is an example stacktrace:

[ERROR] AttributeError: 'NoneType' object has no attribute 'items'
Traceback (most recent call last):
  File "/var/task/lambda_function.py", line 79, in lambda_handler
    return awsgi.response(app, event, context)
  File "/var/task/awsgi/__init__.py", line 159, in response
    output = app(environ(event, context), sr)
  File "/var/task/awsgi/__init__.py", line 129, in environ
    for k, v in headers.items():

After looking at the code, I think it can be fixed by only entering line 129 (the for loop) if items is not None or has a non-zero length.

Please let me know if you have any questions or need more information.

flask_compress support

Hi - great library!

Any way to get it working with flask_compress?

#!/usr/bin/env python
import awsgi
from flask import Flask
from flask import jsonify
from flask_compress import Compress

app = Flask(__name__)
app.config['COMPRESS_MIN_SIZE'] = 0
Compress(app)


@app.route('/')
def index():
    return jsonify(status=200, message='OK')


def lambda_handler(event, context):
    return awsgi.response(app, event, context)


print(lambda_handler({
    'body': None,
    'httpMethod': 'GET',
    'path': '/',
    'queryStringParameters': {},
    'headers': {
        'host': 'localhost',
        'x-forwarded-proto': 'http',
        'Accept-Encoding': 'gzip'
    }
}, None))

Fails with

Traceback (most recent call last):
  File "./test_compress.py", line 31, in <module>
    }, None))
  File "./test_compress.py", line 18, in lambda_handler
    return awsgi.response(app, event, context)
  File "/Users/rog555/Library/Python/2.7/lib/python/site-packages/awsgi/__init__.py", line 22, in response
    return sr.response(output)
  File "/Users/rog555/Library/Python/2.7/lib/python/site-packages/awsgi/__init__.py", line 40, in response
    'body': self.body.getvalue() + ''.join(map(convert_str, output)),
UnicodeDecodeError: 'ascii' codec can't decode byte 0x8b in position 1: ordinal not in range(128)

using aws-wsgi-0.0.7

"URL cannot include query-string parameters (after '?')" error

Description

I'm trying to host a Python/Django project on AWS Lambda. But when I connect to Django's admin, I get a 403 error:

Content-Type:application/x-www-form-urlencoded, URL cannot include query-string parameters (after '?'): '/admin/login/?next=/admin/'

NB: I did't have this problem with the Serverless framework

Request content

POST https://domain.tld/admin/login/?next=/admin/

Request headers

Host: domain.tld
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 138
Connection: keep-alive
Referer: https://domain.tld/admin/login/?next=/admin/
Cookie: csrftoken=A5r8Xxnv0x6EWHOYJEq3zZvMyGCoRWU5ozSfOXaArmWwD4LYFHuYEsStt4Bb9ugB
Upgrade-Insecure-Requests: 1
TE: Trailers

Response headers

HTTP/2.0 403 Forbidden
content-type: application/json
content-length: 155
date: Thu, 05 Sep 2019 14:32:22 GMT
x-amzn-requestid: 05341664-1cc7-4cc6-951d-8f229820d86b
x-amzn-errortype: IncompleteSignatureException
x-amz-apigw-id: fjFicHuZjoEFg3Q=
x-cache: Error from cloudfront
via: 1.1 xxx.cloudfront.net (CloudFront)
x-amz-cf-pop: CDG3-C2
x-amz-cf-id: Xdkmr_gSedl-l2jbkh-dlj8dJzR8ZwxPnkd2n49KWALfRboG5sJbMA==
X-Firefox-Spdy: h2

Response payload

{"message":"When Content-Type:application/x-www-form-urlencoded, URL cannot include query-string parameters (after '?'): '/admin/login/?next=/admin/'"}

Code

app.py

import awsgi

from .wsgi import application

def lambda_handler(event, context):
    return awsgi.response(application, event, context)

template.yaml

---
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: My project stack

Parameters:
  AwsLocation:
    Type: String

  DatabaseUrl:
    Type: String

Globals:
  Function:
    Timeout: 10
    Runtime: python3.6

Resources:
  SuggestionFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: project.app.lambda_handler
      Policies:
        - VPCAccessPolicy: {}

      VpcConfig:
        SecurityGroupIds:
          - xxx
        SubnetIds:
          - xxx
          - xxx
          - xxx

      Events:
        Base:
          Type: Api
          Properties:
            Path: /
            Method: get

        Proxy:
          Type: Api
          Properties:
            Path: /{proxy+}
            Method: get

      Environment:
        Variables:
          AWS_LOCATION: !Ref AwsLocation
          DATABASE_URL: !Ref DatabaseUrl

Outputs:
  EndpointUrl:
    Description: API Gateway endpoint URL
    Value: !Sub https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/

Additional environment details (Ex: Windows, Mac, Amazon Linux etc)

  • Ubuntu 18.04
  • SAM CLI, version 0.21.0
  • Python 3.6.8
  • Django 2.2.4
  • AWSGI as WSGI adapter for AWS API Gateway/Lambda Proxy Integration

POST not encoded correctly for django

python 3.6.3
django 2.0.2
awsgi 0.0.7

here my view is just trying to print POST

Traceback (most recent call last):
  File "/var/task/django/core/handlers/exception.py", line 35, in inner
    response = get_response(request)
  File "/var/task/django/core/handlers/base.py", line 128, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/var/task/django/core/handlers/base.py", line 126, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/var/task/doer/views.py", line 126, in deploy_start
    print(request.POST)
  File "/var/task/django/core/handlers/wsgi.py", line 115, in _get_post
    self._load_post_and_files()
  File "/var/task/django/http/request.py", line 302, in _load_post_and_files
    self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
  File "/var/task/django/http/request.py", line 263, in body
    self._body = self.read()
  File "/var/task/django/http/request.py", line 322, in read
    return self._stream.read(*args, **kwargs)
  File "/var/task/django/core/handlers/wsgi.py", line 36, in read
    result = self.buffer + self._read_limited()
TypeError: can't concat bytes to str

it seams django expects BytesIO for wsgi.input

Is there a way to access event and context inside the flask route

@app.route(BASE_ROUTE, methods=['GET'])
def hello_world():
    # event=request.environ['event']
    print('--hello--')
    # print(event)
    return jsonify(message="hello world")

def handler(event, context):
    print('---event--')
    print(event)
    print('---context--')
    print(context)
    return awsgi.response(app, event, context)

Above is my code structure, I was wondering how can I access the event and context inside the route.
I have a custom authenticated API gateway settings which returns a token inside the event, I want to ask that token across the flask application

'NoneType' object is not iterable

Getting error below since upgrading to 0.1.0:

[ERROR] TypeError: 'NoneType' object is not iterable
Traceback (most recent call last):
  File "/var/task/lambda.py", line 11, in handler
    return awsgi.response(app, event, context)
  File "/var/task/vendor/awsgi/__init__.py", line 25, in response
    sr = StartResponse(base64_content_types=base64_content_types)
  File "/var/task/vendor/awsgi/__init__.py", line 41, in __init__
    self.base64_content_types = set(base64_content_types) or set()

New release?

Current pip release is lacking way back than master. Can we make a release some time soon? Thanks

Base path mapping of API GW custom domains is added to path

I'm not sure what the proper solution for this is but,
If you set up custom API gateway domain with a base path mapping, then the path mapping and stage are added to the path flask receives. So instead of it dispatching on /foo/bar it dispatches on /v1/Prod/foo/bar.

screen shot 2018-04-29 at 14 35 56

If I go to https://qanda.llolo.lol/v1/Prod/foo/bar - then I get a 404 because it tries to send it to /v1/Prod/foo/bar instead of /foo/bar.

What's the best way to deal with this?

Error with sending excel file with send_file

Hello everyone,

I am getting errors from my code below:

output = BytesIO()
writer = pd.ExcelWriter(output, engine='xlsxwriter')  
df.to_excel(writer, startrow = 0, merge_cells = False, sheet_name = "results", index=False)
workbook  = writer.book
worksheet = writer.sheets["results"]    
worksheet.set_column(1, 20, 20)           
writer.save()          
output.seek(0)
return send_file(output, attachment_filename="test.xlsx", as_attachment=True)

The error message is

'utf-8' codec can't decode byte 0xb6 in position 14: invalid start byte: UnicodeDecodeError
Traceback (most recent call last):
File "/var/task/handler.py", line 60, in run
return awsgi.response(app, event, context)
File "/var/task/awsgi/__init__.py", line 22, in response
return sr.response(output)
File "/var/task/awsgi/__init__.py", line 41, in response
'body': self.body.getvalue() + ''.join(map(convert_str, output)),
File "/var/task/awsgi/__init__.py", line 9, in convert_str
return s.decode('utf-8') if isinstance(s, bytes) else s
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb6 in position 14: invalid start byte

With some research, I found the file __ init __.py line 9 should be updated

from:
return s.decode('utf-8') if isinstance(s, bytes) else s
to:
return s.decode('utf-8', 'ignore') if isinstance(s, bytes) else s

Tested the code above and it's now working correctly. The excel file is being download as expected.

May I make a pull request and submit this change?

Thank you

[ERROR] KeyError: 'httpMethod'

Trying to integrate Flask with AWS Lambda through SAM. However, I am getting 404 Error when I hit the API.

Using Windows, with Flask, awsgi, aws-cli and aws-sam. Python 3.11.

My app.py:

import awsgi
from flask import (
    Flask,
    jsonify,
)

app = Flask(__name__)


@app.route('/')
def index():
    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": "hello world",
            # "location": ip.text.replace("\n", "")
        }),
    }

def lambda_handler(event, context):
    return awsgi.response(app, event, context)

Steps to reproduce:
sam build

sam deploy --guided

cURL https://**********.execute-api.us-east-1.amazonaws.com/Prod/bot/

Expected output"
{ "message": "hello world"}

What I get is 404 Error.
If I run sam remote invoke HelloWorldFunction --stack-name sam-flask, I get:

Invoking Lambda Function HelloWorldFunction
START RequestId: e0d920ad-cc19-4292-bf69-a1d148bfc98e Version: $LATEST
[ERROR] KeyError: 'httpMethod'
Traceback (most recent call last):
  File "/var/task/app.py", line 21, in lambda_handler
    return awsgi.response(app, event, context)
  File "/var/task/awsgi/__init__.py", line 172, in response
    output = app(environ(event, context), sr)
  File "/var/task/awsgi/__init__.py", line 121, in environ
    'REQUEST_METHOD': event['httpMethod'],END RequestId: e0d920ad-cc19-4292-bf69-a1d148bfc98e
REPORT RequestId: e0d920ad-cc19-4292-bf69-a1d148bfc98e  Duration: 12.56 ms      Billed Duration: 13 ms  Memory Size: 128 MB     Max Memory Used: 63 MB  Init Duration: 557.31 ms
{"errorMessage": "'httpMethod'", "errorType": "KeyError", "requestId": "e0d920ad-cc19-4292-bf69-a1d148bfc98e", "stackTrace": ["  File \"/var/task/app.py\", line 21, in lambda_handler\n    return awsgi.response(app, event, context)\n", "  File \"/var/task/awsgi/__init__.py\", line 172, in response\n    output = app(environ(event, context), sr)\n", "  File \"/var/task/awsgi/__init__.py\", line 121, in environ\n    'REQUEST_METHOD': event['httpMethod'],\n"]}

I feel it is something simple I am missing.
If I replace lambda_handler with the tutorial code from AWS I get expected output.
This below is that code. So my AWS Lambda Proxy Resource is configured correctly for simple JSON but not for awsgi.

def lambda_handler(event, context):
    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": "hello world",
            # "location": ip.text.replace("\n", "")
        }),
    }

What am I doing wrong? Can anyone help? I have been trying for a week now.

Help Wanted

I'd like to humbly acknowledge that I have not actively maintained this project for some time. I don't expect my time commitments to change any time soon, so I'd like to ask for some interested parties to maintain and eventually take ownership.

If you have the time and patience to maintain awsgi, please use this issue to volunteer and discuss. Thanks.

Wrong CONTENT_LENGTH when event.body contains non-ascii

We run a flask application as lambda behind an api gateway using awsgi. This flask application reads the body as json with flasks request.get_json(). This bails out with a JSON error:
Failed to decode JSON object: Expecting ',' delimiter: line X column Y (char Z)

This is caused because awsgi sets the CONTENT_LENGTH in the environ to len(event.get('body', '')) but it should set it to the len of the encoded bytes string.

Call that works as expected:
curl -v -X PATCH -H 'Content-Type: application/json' http://127.0.0.1:3000/utf8test -d '{"test": "Jagermeister"}'

Call that has this error as result:
curl -v -X PATCH -H 'Content-Type: application/json' http://127.0.0.1:3000/utf8test -d '{"test": "Jägermeister"}'

Flask redirect doesn't work

The flask util function redirect(url) does not translate to a redirect in AWS Gateway. Using flask.Response( status=302, headers={"Location": url }) does work though (using the latest version). If someone can give me a hint on where to fix this, I might be able to add a PR. Thanks and cheers!

Use wsgiref.headers.Headers

HTTP headers have some semantics that aren't compatible with a naive dict.

Use wsgiref.headers.Headers to handle this.

SERVER_NAME missing

  File "/var/task/vendor/awsgi/__init__.py", line 21, in response
    output = app(environ(event, context), sr)
  File "/var/task/vendor/flask/app.py", line 1997, in __call__
    return self.wsgi_app(environ, start_response)
  File "/var/task/vendor/flask/app.py", line 1977, in wsgi_app
    ctx = self.request_context(environ)
  File "/var/task/vendor/flask/app.py", line 1938, in request_context
    return RequestContext(self, environ)
  File "/var/task/vendor/flask/ctx.py", line 242, in __init__
    self.url_adapter = app.create_url_adapter(self.request)
  File "/var/task/vendor/flask/app.py", line 1765, in create_url_adapter
    server_name=self.config['SERVER_NAME'])
  File "/var/task/vendor/werkzeug/routing.py", line 1307, in bind_to_environ
    wsgi_server_name = environ['SERVER_NAME']
KeyError: 'SERVER_NAME'

Using sam local start-api

super().__init__ can not be used in python2

Hi, I used awsgi to deploy a flask app on AWS Lambda, the run time is python2.7, then I got a type error:

Traceback (most recent call last):
  File "/var/task/app.py", line 19, in lambda_handler
    return awsgi.response(app, event, context, base64_content_types={"image/png"})
  File "/var/task/awsgi/__init__.py", line 169, in response
    return sr.response(output)
  File "/var/task/awsgi/__init__.py", line 91, in response
    rv = super().response(output)
TypeError: super() takes at least 1 argument (0 given)

so I edited the file as follows:

class StartResponse(object):  # use new-style class in python2
...
rv = super(StartResponse_GW, self).response(output)
...
rv = super(StartResponse_ELB, self).response(output)

so now it can work in python2.7

[proposed procedures] add awsgi into Lambda function directly

  1. create a folder "awsgi" under the main folder (normally, it is the same name as the function name)
  2. create a blank file "init.py" (it seems not showing correctly, that is "__ init __ .py", no space between)
  3. create another file "awsgi.py"
  4. open the file awsgi.py, copy the code from this repository slank/awsgi/awsgi/init.py, replace the code provided from the issue #67 if you are using HTTP API
  5. in the main code, should be lambda_function.py, add "from awsgi import awsgi"
  6. that's it...

Update PyPI package

Hello there,

Is there a timeframe of when the package on PyPI will be updated to version 0.2.4? I encountered an issue when posting JSON data with non-English characters that was fixed in the latest commit. But that code is not included in version 0.2.3 of the package.

Is there support for @app.errorhandler()?

When my flask app hits an error, the app seems to always return a response code of 200 with a body containing the relevant HTML to explain what error has occurred. I should be able to catch errors with @app.errorhandler(), but it does not seem to be working. Is this supported?

I would expect the response code to not be 200 if a 404 was thrown...

TypeError: argument of type 'NoneType' is not iterable

Getting the following error with 0.2.6

[ERROR] TypeError: argument of type 'NoneType' is not iterable
Traceback (most recent call last):
  File "/var/task/app/app.py", line 21, in entrypoint
    return awsgi.response(api, event, context)
  File "/var/task/awsgi/__init__.py", line 172, in response
    return sr.response(output)
  File "/var/task/awsgi/__init__.py", line 94, in response
    rv = super(StartResponse_GW, self).response(output)
  File "/var/task/awsgi/__init__.py", line 88, in response
    rv.update(self.build_body(headers, output))
  File "/var/task/awsgi/__init__.py", line 69, in build_body
    is_b64 = self.use_binary_response(headers, totalbody)
  File "/var/task/awsgi/__init__.py", line 60, in use_binary_response
    if ';' in content_type:

Appears directly related to #50

TypeError on Python3.6 runtime

Since Lambda supported Python 3.6, I tried updating Flask's application to Python 3.6, but TypeError occurred.

must be str, not bytes: TypeError
Traceback (most recent call last):
File "/var/task/main.py", line 9, in lambda_handler
return awsgi.response(app, event, context)
File "/var/task/awsgi/__init__.py", line 14, in response
return sr.response(output)
File "/var/task/awsgi/__init__.py", line 35, in response
resp['body'] += chunk
TypeError: must be str, not bytes

resp['body'] is <class 'str'>, but output is <class 'bytes'>.
So, it has failed to combine strings.

I was not familiar with WSGI so I did not know how to concatenate it correctly, but I am glad that the error is resolved.


AWS Lambda Supports Python 3.6
https://aws.amazon.com/jp/about-aws/whats-new/2017/04/aws-lambda-supports-python-3-6/

How to return GZIP Content-Encoding

Hi,

I am trying to return a GZIP encoded response but getting the error :

{
    "errorMessage": "'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte",
    "errorType": "UnicodeDecodeError",
    "stackTrace": [
        "  File \"/var/task/lambda_handler.py\", line 75, in handler\n    return awsgi.response(app, event, context)\n",
        "  File \"/var/task/awsgi/__init__.py\", line 22, in response\n    return sr.response(output)\n",
        "  File \"/var/task/awsgi/__init__.py\", line 41, in response\n    'body': self.body.getvalue() + ''.join(map(convert_str, output)),\n",
        "  File \"/var/task/awsgi/__init__.py\", line 9, in convert_str\n    return s.decode('utf-8') if isinstance(s, bytes) else s\n"
    ]
}

Is there any way to skip the response transformed in String ?

'REQUEST_METHOD': event['httpMethod'] KeyError

Invoking Container created from helloworldfunction:0.0.1-prod
Building image.................
Skip pulling image and use local one: helloworldfunction:rapid-1.27.2.

START RequestId: 8ebeeb37-1879-40fc-aed7-99902d8541a0 Version: $LATEST
    'REQUEST_METHOD': event['httpMethod'], 121, in environe
END RequestId: 8ebeeb37-1879-40fc-aed7-99902d8541a0
REPORT RequestId: 8ebeeb37-1879-40fc-aed7-99902d8541a0  Init Duration: 0.11 ms  Duration: 170.98 ms     Billed Duration: 200 ms Memory Size: 128 MB     Max Memory Used: 128 MB
{"errorMessage": "'httpMethod'", "errorType": "KeyError", "stackTrace": ["  File \"/var/task/app.py\", line 6, in lambda_handler\n    return awsgi.response(app, event, context)\n", "  File \"/var/task/awsgi/__init__.py\", line 172, in
response\n    output = app(environ(event, context), sr)\n", "  File \"/var/task/awsgi/__init__.py\", line 121, in environ\n    'REQUEST_METHOD': event['httpMethod'],\n"]}sbhakunamatata@DESKTOP-KV7TUSU:/mnt/c/Users/Shrir/PycharmProjects
/jwtFlashRestTutorial$ 

Getting this error when doing sam local invoke while using awsgi

The lambdahandler:

def lambda_handler(event, context):
    return awsgi.response(app, event, context)

creating flask app like:

from flask import Flask

app: Flask = Flask(__name__)

support broken parameter escaping with load balancer

When lambdas are behind an ALB instead of API gateway, path and query parameters don't get unquoted.

(example issue here dougmoscrop/serverless-http#74)

I fixed this for myself with:

def lambda_handler(event, context):
  "entrypoint for lambda"
  params = event['queryStringParameters']
  for key in params:
    # todo: keys should probably be unquoted as well? didn't test
    params[key] = url.parse.unquote(params[key])
  event['path'] = urllib.parse.unquote(event['path'])
  return awsgi.response(app, event, context, base64_content_types={"image/png", "image/x-icon", "image/vnd.microsoft.icon"})

Happy to submit a PR if useful, either adding docs or adding a kwarg to awsgi.response. LMK.

Also, great project and thanks for your work.

Better Documentation

There seems to be something lacking in either the API-Gateway setup docs or the awsgi docs. I have copied the code directly from the example and I cannot get it to work. I am using the aws sam cli for testing. I can get the generic un-styled Flask 404 response but nothing else.

As far as I can tell the lambda handler is handling, but the request is never handed to flask.

[New requirement] Solution to make awsgi working with Http Api that invoke Lambda functions

Hello,

This package works fine when I create Rest API for the Lambda function by using Amazon API Gateway or AWS SAM, but not with Http API type . It is very easy to notice this gap. To do this, just create an API that invoke the Lambda function with HTTP API type instead of REST API type.

The reason that make this package not working with HTTP API type is that HTTP APIs are newer and are built with the API Gateway version 2 API. So, the event json structure is different than the previous one. By the way, the http Method and the path are specified by the following paths:

  • requestContext.http.method instead of httpMethod
  • requestContext.http.path instead of path

For more details, please look into the following link (paragraph: Choosing an API type)
Using AWS Lambda with Amazon API Gateway

To deal with that, I think we have no choice, we should modify the function def environ (event, context) a bit. And here are my retrocompatible modifications:

def environ(event, context):
    body = event.get('body', '') or ''

    if event.get('isBase64Encoded', False):
        body = b64decode(body)
    # FIXME: Flag the encoding in the headers
    body = convert_byte(body)

    httpMethod = ''
    path = ''
    if 'httpMethod' in event and 'path' in event:
        httpMethod =  event['httpMethod']
        path =  event['path']
    elif 'http' in event['requestContext']:
        if 'method' in event['requestContext']['http']:
            httpMethod =  event['requestContext']['http']['method']
        if 'path' in event['requestContext']['http']:
            path =  event['requestContext']['http']['path']

    queryStringParameters = ''
    if 'queryStringParameters' in event and event['queryStringParameters'] is not None:
        queryStringParameters = urlencode(event['queryStringParameters'])
    
    environ = {
        'REQUEST_METHOD': httpMethod,
        'SCRIPT_NAME': '',
        'SERVER_NAME': '',
        'SERVER_PORT': '',
        'PATH_INFO': path ,
        'QUERY_STRING':queryStringParameters,
        'REMOTE_ADDR': '127.0.0.1',
        'CONTENT_LENGTH': str(len(body)),
        'HTTP': 'on',
        'SERVER_PROTOCOL': 'HTTP/1.1',
        'wsgi.version': (1, 0),
        'wsgi.input': BytesIO(body),
        'wsgi.errors': sys.stderr,
        'wsgi.multithread': False,
        'wsgi.multiprocess': False,
        'wsgi.run_once': False,
        'wsgi.url_scheme': '',
        'awsgi.event': event,
        'awsgi.context': context,
    }
    headers = event.get('headers', {}) or {}
    for k, v in headers.items():
        k = k.upper().replace('-', '_')

        if k == 'CONTENT_TYPE':
            environ['CONTENT_TYPE'] = v
        elif k == 'HOST':
            environ['SERVER_NAME'] = v
        elif k == 'X_FORWARDED_FOR':
            environ['REMOTE_ADDR'] = v.split(', ')[0]
        elif k == 'X_FORWARDED_PROTO':
            environ['wsgi.url_scheme'] = v
        elif k == 'X_FORWARDED_PORT':
            environ['SERVER_PORT'] = v

        environ['HTTP_' + k] = v

    return environ

Of course I've tested (locally with aws sam and in AWS console after deployment) that this code enhancement is working properly.

Best regards,

Pass raw AWS Lambda event via the WSGI environ dictionary

I've been happily using awsgi until encountering an issue when using API Gateway custom authorizers. The authorizer context, which allows your authorizer to pass custom data to the application (e.g. user ID, group membership, permissions, etc.), exists as a dictionary value in the Lambda event. It lives in event['requestContext']['authorizer'], but this value is not passed by awsgi to the application.

Since AWS could at any time add other bits of useful metadata to the Lambda event, would it make sense for awsgi to just pass the AWS Lambda event to the application via the WSGI environ dictionary?

Missing wsgi.url_scheme

Flask seems to require wsgi.url_scheme in the WSGI environ dict.

[ERROR] KeyError: 'wsgi.url_scheme'
Traceback (most recent call last):
  File "/var/task/__main__.py", line 17, in main
    return awsgi.response(app, event, context, base64_content_types={"image/png"})
  File "/var/task/awsgi/__init__.py", line 26, in response
    output = app(environ(event, context), sr)
  File "/var/task/flask/app.py", line 2463, in __call__
    return self.wsgi_app(environ, start_response)
  File "/var/task/flask/app.py", line 2441, in wsgi_app
    ctx = self.request_context(environ)
  File "/var/task/flask/app.py", line 2358, in request_context
    return RequestContext(self, environ)
  File "/var/task/flask/ctx.py", line 292, in __init__
    self.url_adapter = app.create_url_adapter(self.request)
  File "/var/task/flask/app.py", line 2173, in create_url_adapter
    subdomain=subdomain,
  File "/var/task/werkzeug/routing.py", line 1502, in bind_to_environ
    wsgi_server_name = get_host(environ).lower()
  File "/var/task/werkzeug/wsgi.py", line 170, in get_host
    if (environ["wsgi.url_scheme"], environ["SERVER_PORT"]) not in (

This is in violation of PEP 3333:

In addition to the CGI-defined variables, the environ dictionary ... must contain the following WSGI-defined variables:

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.