LoginSignup
0
0

APIを呼び出すLambdaで、リクエストヘッダーを全部送るとRuntime.MarshalErrorが起きる

Posted at

起こったこと

自分で開発したAPIを叩いて、戻ってきたレスポンスをそのまま返すAWS Lambdaを作っていたのですが、固定のヘッダーをつけてリクエストするときは問題なく呼び出せるのに、API Gatewayに渡されたリクエストヘッダーをそのままつけてリクエストすると、原因どころか発生箇所すらもよく分からないエラーが起きるようになりました。

lambda_function.py
import json
import urllib.request

def lambda_handler(event, context):
 
    headers = event.get('headers')
    request_body = event.get('body').encode('utf-8')
 
    # リクエスト送信
    req = urllib.request.Request('https://xxxx/api/v1/yyyy', headers=headers, method='POST', data=request_body)
    with urllib.request.urlopen(req) as res:
        body = res.read()
        code = res.getcode()
 
    return {
        'statusCode': code,
        'body': body
    }

[ERROR] Runtime.MarshalError: Unable to marshal response: 'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte
Traceback (most recent call last):

なぜかスタックトレースが何も表示されない…

APIのレスポンスが圧縮されていた

このエラーメッセージについて調べてみたところ、ひとつの鍵はbyte 0x8b in position 1でした。
python - UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte, while reading csv file in pandas - Stack Overflow
0x8bはgzipで使われるマジックナンバーとのこと。gzip圧縮されたレスポンスは確かにUTF-8デコードできません。
そういえば、このAPIはASP.NETで開発しており、応答圧縮を有効にしていたのでした。
この機能はクライアントから送られたリクエストヘッダーのAccept-Encodingを見て、レスポンスを圧縮していいかを判断しています。
実際に、送ろうとしたヘッダーには以下が含まれていました。
'accept-encoding': 'gzip, deflate, br'
このヘッダーを除外してAPIを呼び出すことで、レスポンスの圧縮を防ぎ、問題を回避することができました。

    headers = event.get('headers')
+   headers.pop('accept-encoding', None)    # 応答圧縮を回避
    request_body = event.get('body').encode('utf-8')

エラーはLambdaランライムで起きていた

直接の原因と回避策は分かったものの、Lambdaのコードの中でUTF-8デコードしている箇所がないのになぜこのエラーが起きるのか、気持ち悪さが残ります。
しかも、エラーのスタックトレースが何もないので、発生箇所が分かりません。
上記のプログラムにログを仕込んで調べたところ、returnの直前まで実行されていることが分かりました。
Runtime.MarshalErrorについて調べたところ、Lambdaは戻り値をJSONシリアライズしてから呼び出し元に返しており、戻り値がシリアライズできない場合にこのエラーが起きることが分かりました。
Python の Lambda 関数ハンドラー
スタックトレースが何もなかったのは、エラーの発生箇所がLambdaラインタイムだったからのようです。

ちなみに、戻り値をBase64エンコードしてから返せばこのエラーを回避できるとの情報もありましたが、試していません。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0