in lambda/handler.go [290:395]
func reflectHandler(f interface{}, h *handlerOptions) handlerFunc {
if f == nil {
return errorHandler(errors.New("handler is nil"))
}
// back-compat: types with reciever `Invoke(context.Context, []byte) ([]byte, error)` need the return bytes wrapped
if handler, ok := f.(Handler); ok {
return func(ctx context.Context, payload []byte) (io.Reader, error) {
b, err := handler.Invoke(ctx, payload)
if err != nil {
return nil, err
}
return bytes.NewBuffer(b), nil
}
}
handler := reflect.ValueOf(f)
handlerType := reflect.TypeOf(f)
if handlerType.Kind() != reflect.Func {
return errorHandler(fmt.Errorf("handler kind %s is not %s", handlerType.Kind(), reflect.Func))
}
takesContext, err := handlerTakesContext(handlerType)
if err != nil {
return errorHandler(err)
}
if err := validateReturns(handlerType); err != nil {
return errorHandler(err)
}
out := &jsonOutBuffer{bytes.NewBuffer(nil)}
return func(ctx context.Context, payload []byte) (io.Reader, error) {
out.Reset()
in := bytes.NewBuffer(payload)
decoder := json.NewDecoder(in)
if h.jsonRequestUseNumber {
decoder.UseNumber()
}
if h.jsonRequestDisallowUnknownFields {
decoder.DisallowUnknownFields()
}
encoder := json.NewEncoder(out)
encoder.SetEscapeHTML(h.jsonResponseEscapeHTML)
encoder.SetIndent(h.jsonResponseIndentPrefix, h.jsonResponseIndentValue)
trace := handlertrace.FromContext(ctx)
// construct arguments
var args []reflect.Value
if takesContext {
args = append(args, reflect.ValueOf(ctx))
}
if (handlerType.NumIn() == 1 && !takesContext) || handlerType.NumIn() == 2 {
eventType := handlerType.In(handlerType.NumIn() - 1)
event := reflect.New(eventType)
if err := decoder.Decode(event.Interface()); err != nil {
return nil, err
}
if nil != trace.RequestEvent {
trace.RequestEvent(ctx, event.Elem().Interface())
}
args = append(args, event.Elem())
}
response := handler.Call(args)
// return the error, if any
if len(response) > 0 {
if errVal, ok := response[len(response)-1].Interface().(error); ok && errVal != nil {
return nil, errVal
}
}
// set the response value, if any
var val interface{}
if len(response) > 1 {
val = response[0].Interface()
if nil != trace.ResponseEvent {
trace.ResponseEvent(ctx, val)
}
}
// encode to JSON
if err := encoder.Encode(val); err != nil {
// if response is not JSON serializable, but the response type is a reader, return it as-is
if reader, ok := val.(io.Reader); ok {
return reader, nil
}
return nil, err
}
// if response value is an io.Reader, return it as-is
if reader, ok := val.(io.Reader); ok {
// back-compat, don't return the reader if the value serialized to a non-empty json
if strings.HasPrefix(out.String(), "{}") {
return reader, nil
}
}
// back-compat, strip the encoder's trailing newline unless WithSetIndent was used
if h.jsonResponseIndentValue == "" && h.jsonResponseIndentPrefix == "" {
out.Truncate(out.Len() - 1)
}
return out, nil
}
}