Skip to content

Custom context for function handlers result in panic #377

Closed
@d-tsuji

Description

@d-tsuji

Is your feature request related to a problem? Please describe.

In the AWS documentation, the handler for the function is shown to have the following signature

  • func ()
  • func () error
  • func (TIn) error
  • func () (TOut, error)
  • func (TIn) (TOut, error)
  • func (context.Context) error
  • func (context.Context, TIn) error
  • func (context.Context) (TOut, error)
  • func (context.Context, TIn) (TOut, error)

https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html

But it also says the following.

The handler may take between 0 and 2 arguments. If there are two arguments, the first argument must implement context.Context.


The first argument in the signature of the function that takes two arguments is clearly context.Context, but the panic occurs when customContext is taken as an argument.

For example, deploy and execute the following code to AWS Lambda.

package main

import (
	"context"
	"log"

	"github.com/aws/aws-lambda-go/lambda"
)

func main() {
	lambda.Start(Handle)
}

func Handle(ctx customContext) {
	log.Println("hello")
	return
}

// customContext satisfies "context.Context"
type customContext struct {
	context.Context
}

When this Lambda is executed, a panic occurs as shown below.

reflect: Call using *context.valueCtx as type main.customContext: string
[
	{
		"path": "github.com/aws/[email protected]/lambda/errors.go",
		"line": 39,
		"label": "lambdaPanicResponse"
	},
	{
		"path": "github.com/aws/[email protected]/lambda/function.go",
		"line": 36,
		"label": "(*Function).Invoke.func1"
	},
	{
		"path": "runtime/panic.go",
		"line": 965,
		"label": "gopanic"
	},
	{
		"path": "reflect/value.go",
		"line": 406,
		"label": "Value.call"
	},
	{
		"path": "reflect/value.go",
		"line": 337,
		"label": "Value.Call"
	},
	{
		"path": "github.com/aws/[email protected]/lambda/handler.go",
		"line": 124,
		"label": "NewHandler.func1"
	},
	{
		"path": "github.com/aws/[email protected]/lambda/handler.go",
		"line": 24,
		"label": "lambdaHandler.Invoke"
	},
	{
		"path": "github.com/aws/[email protected]/lambda/function.go",
		"line": 64,
		"label": "(*Function).Invoke"
	},
	{
		"path": "reflect/value.go",
		"line": 476,
		"label": "Value.call"
	},
	{
		"path": "reflect/value.go",
		"line": 337,
		"label": "Value.Call"
	},
	{
		"path": "net/rpc/server.go",
		"line": 377,
		"label": "(*service).call"
	},
	{
		"path": "runtime/asm_amd64.s",
		"line": 1371,
		"label": "goexit"
	}
]

Describe the solution you'd like

If we take context.Context as an argument, we need to make sure that the first argument of the function signature is context.Context explicitly.

contextType := reflect.TypeOf((*context.Context)(nil)).Elem()
argumentType := handler.In(0)
handlerTakesContext = argumentType.Implements(contextType)

The above implementation only checks if the argument satisfies context.Context, so if a custom context that satisfies context.Context is the first argument, it cannot be checked by the above validation.

Describe alternatives you've considered

I wasn't sure if it was correct to explicitly accept only context.Context as a signature, or if we will accept a custom context satisfying context.Context as a function signature.

Additional context

Nothing.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions