-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge from aws-lambda-runtime-interface-emulator/develop (#40)
* Pull upstream changes (#34) * Pull upstream changes 2021/06 (#39) * docs: Fix ordering of options in example (#36) Co-authored-by: Jon Colverson <[email protected]>
- Loading branch information
Showing
48 changed files
with
1,714 additions
and
316 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package directinvoke | ||
|
||
import ( | ||
"bytes" | ||
"encoding/base64" | ||
"encoding/json" | ||
) | ||
|
||
type CustomerHeaders struct { | ||
CognitoIdentityID string `json:"Cognito-Identity-Id"` | ||
CognitoIdentityPoolID string `json:"Cognito-Identity-Pool-Id"` | ||
ClientContext string `json:"Client-Context"` | ||
} | ||
|
||
func (s CustomerHeaders) Dump() string { | ||
if (s == CustomerHeaders{}) { | ||
return "" | ||
} | ||
|
||
custHeadersJSON, err := json.Marshal(&s) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
return base64.StdEncoding.EncodeToString(custHeadersJSON) | ||
} | ||
|
||
func (s *CustomerHeaders) Load(in string) error { | ||
*s = CustomerHeaders{} | ||
|
||
if in == "" { | ||
return nil | ||
} | ||
|
||
base64Decoder := base64.NewDecoder(base64.StdEncoding, bytes.NewReader([]byte(in))) | ||
|
||
return json.NewDecoder(base64Decoder).Decode(s) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package directinvoke | ||
|
||
import ( | ||
"github.com/stretchr/testify/require" | ||
"testing" | ||
) | ||
|
||
func TestCustomerHeadersEmpty(t *testing.T) { | ||
in := CustomerHeaders{} | ||
out := CustomerHeaders{} | ||
|
||
require.NoError(t, out.Load(in.Dump())) | ||
require.Equal(t, in, out) | ||
} | ||
|
||
func TestCustomerHeaders(t *testing.T) { | ||
in := CustomerHeaders{CognitoIdentityID: "asd"} | ||
out := CustomerHeaders{} | ||
|
||
require.NoError(t, out.Load(in.Dump())) | ||
require.Equal(t, in, out) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package directinvoke | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"net/http" | ||
|
||
"github.com/go-chi/chi" | ||
"go.amzn.com/lambda/interop" | ||
"go.amzn.com/lambda/metering" | ||
) | ||
|
||
const ( | ||
InvokeIDHeader = "Invoke-Id" | ||
InvokedFunctionArnHeader = "Invoked-Function-Arn" | ||
VersionIDHeader = "Invoked-Function-Version" | ||
ReservationTokenHeader = "Reservation-Token" | ||
CustomerHeadersHeader = "Customer-Headers" | ||
ContentTypeHeader = "Content-Type" | ||
|
||
ErrorTypeHeader = "Error-Type" | ||
|
||
EndOfResponseTrailer = "End-Of-Response" | ||
|
||
SandboxErrorType = "Error.Sandbox" | ||
) | ||
|
||
const ( | ||
EndOfResponseComplete = "Complete" | ||
EndOfResponseTruncated = "Truncated" | ||
EndOfResponseOversized = "Oversized" | ||
) | ||
|
||
var MaxDirectResponseSize int64 = interop.MaxPayloadSize // this is intentionally not a constant so we can configure it via CLI | ||
|
||
func renderBadRequest(w http.ResponseWriter, r *http.Request, errorType string) { | ||
w.Header().Set(ErrorTypeHeader, errorType) | ||
w.WriteHeader(http.StatusBadRequest) | ||
w.Header().Set(EndOfResponseTrailer, EndOfResponseComplete) | ||
} | ||
|
||
// ReceiveDirectInvoke parses invoke and verifies it against Token message. Uses deadline provided by Token | ||
// Renders BadRequest in case of error | ||
func ReceiveDirectInvoke(w http.ResponseWriter, r *http.Request, token interop.Token) (*interop.Invoke, error) { | ||
w.Header().Set("Trailer", EndOfResponseTrailer) | ||
|
||
custHeaders := CustomerHeaders{} | ||
if err := custHeaders.Load(r.Header.Get(CustomerHeadersHeader)); err != nil { | ||
renderBadRequest(w, r, interop.ErrMalformedCustomerHeaders.Error()) | ||
return nil, interop.ErrMalformedCustomerHeaders | ||
} | ||
|
||
now := metering.Monotime() | ||
inv := &interop.Invoke{ | ||
ID: r.Header.Get(InvokeIDHeader), | ||
ReservationToken: chi.URLParam(r, "reservationtoken"), | ||
InvokedFunctionArn: r.Header.Get(InvokedFunctionArnHeader), | ||
VersionID: r.Header.Get(VersionIDHeader), | ||
ContentType: r.Header.Get(ContentTypeHeader), | ||
CognitoIdentityID: custHeaders.CognitoIdentityID, | ||
CognitoIdentityPoolID: custHeaders.CognitoIdentityPoolID, | ||
TraceID: token.TraceID, | ||
LambdaSegmentID: token.LambdaSegmentID, | ||
ClientContext: custHeaders.ClientContext, | ||
Payload: r.Body, | ||
CorrelationID: "invokeCorrelationID", | ||
DeadlineNs: fmt.Sprintf("%d", now+token.FunctionTimeout.Nanoseconds()), | ||
NeedDebugLogs: token.NeedDebugLogs, | ||
InvokeReceivedTime: now, | ||
} | ||
|
||
if inv.ID != token.InvokeID { | ||
renderBadRequest(w, r, interop.ErrInvalidInvokeID.Error()) | ||
return nil, interop.ErrInvalidInvokeID | ||
} | ||
|
||
if inv.ReservationToken != token.ReservationToken { | ||
renderBadRequest(w, r, interop.ErrInvalidReservationToken.Error()) | ||
return nil, interop.ErrInvalidReservationToken | ||
} | ||
|
||
if inv.VersionID != token.VersionID { | ||
renderBadRequest(w, r, interop.ErrInvalidFunctionVersion.Error()) | ||
return nil, interop.ErrInvalidFunctionVersion | ||
} | ||
|
||
if now > token.InvackDeadlineNs { | ||
renderBadRequest(w, r, interop.ErrReservationExpired.Error()) | ||
return nil, interop.ErrReservationExpired | ||
} | ||
|
||
w.Header().Set(VersionIDHeader, token.VersionID) | ||
w.Header().Set(ReservationTokenHeader, token.ReservationToken) | ||
w.Header().Set(InvokeIDHeader, token.InvokeID) | ||
|
||
return inv, nil | ||
} | ||
|
||
func SendDirectInvokeResponse(additionalHeaders map[string]string, payload io.Reader, w http.ResponseWriter) error { | ||
for k, v := range additionalHeaders { | ||
w.Header().Add(k, v) | ||
} | ||
|
||
n, err := io.Copy(w, io.LimitReader(payload, MaxDirectResponseSize+1)) // +1 because we do allow 10MB but not 10MB + 1 byte | ||
if err != nil { | ||
w.Header().Set(EndOfResponseTrailer, EndOfResponseTruncated) | ||
} else if n == MaxDirectResponseSize+1 { | ||
w.Header().Set(EndOfResponseTrailer, EndOfResponseOversized) | ||
} else { | ||
w.Header().Set(EndOfResponseTrailer, EndOfResponseComplete) | ||
} | ||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.