feat(aws): support lambda (#334)
This commit is contained in:
34
vendor/github.com/apex/gateway/Readme.md
generated
vendored
Normal file
34
vendor/github.com/apex/gateway/Readme.md
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<img src="http://tjholowaychuk.com:6000/svg/title/APEX/GATEWAY">
|
||||
|
||||
Package gateway provides a drop-in replacement for net/http's `ListenAndServe` for use in AWS Lambda & API Gateway, simply swap it out for `gateway.ListenAndServe`. Extracted from [Up](https://github.com/apex/up) which provides additional middleware features and operational functionality.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/apex/gateway"
|
||||
)
|
||||
|
||||
func main() {
|
||||
addr := ":" + os.Getenv("PORT")
|
||||
http.HandleFunc("/", hello)
|
||||
log.Fatal(gateway.ListenAndServe(addr, nil))
|
||||
}
|
||||
|
||||
func hello(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "Hello World from Go")
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[](https://godoc.org/github.com/apex/gateway)
|
||||

|
||||

|
||||
|
||||
<a href="https://apex.sh"><img src="http://tjholowaychuk.com:6000/svg/sponsor"></a>
|
||||
33
vendor/github.com/apex/gateway/gateway.go
generated
vendored
Normal file
33
vendor/github.com/apex/gateway/gateway.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// Package gateway provides a drop-in replacement for net/http.ListenAndServe for use in AWS Lambda & API Gateway.
|
||||
package gateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/aws/aws-lambda-go/events"
|
||||
"github.com/aws/aws-lambda-go/lambda"
|
||||
)
|
||||
|
||||
// ListenAndServe is a drop-in replacement for
|
||||
// http.ListenAndServe for use within AWS Lambda.
|
||||
//
|
||||
// ListenAndServe always returns a non-nil error.
|
||||
func ListenAndServe(addr string, h http.Handler) error {
|
||||
if h == nil {
|
||||
h = http.DefaultServeMux
|
||||
}
|
||||
|
||||
lambda.Start(func(ctx context.Context, e events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
|
||||
r, err := NewRequest(ctx, e)
|
||||
if err != nil {
|
||||
return events.APIGatewayProxyResponse{}, err
|
||||
}
|
||||
|
||||
w := NewResponse()
|
||||
h.ServeHTTP(w, r)
|
||||
return w.End(), nil
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
74
vendor/github.com/apex/gateway/request.go
generated
vendored
Normal file
74
vendor/github.com/apex/gateway/request.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
package gateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-lambda-go/events"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// NewRequest returns a new http.Request from the given Lambda event.
|
||||
func NewRequest(ctx context.Context, e events.APIGatewayProxyRequest) (*http.Request, error) {
|
||||
// path
|
||||
u, err := url.Parse(e.Path)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "parsing path")
|
||||
}
|
||||
|
||||
// querystring
|
||||
q := u.Query()
|
||||
for k, v := range e.QueryStringParameters {
|
||||
q.Set(k, v)
|
||||
}
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
// base64 encoded body
|
||||
body := e.Body
|
||||
if e.IsBase64Encoded {
|
||||
b, err := base64.StdEncoding.DecodeString(body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decoding base64 body")
|
||||
}
|
||||
body = string(b)
|
||||
}
|
||||
|
||||
// new request
|
||||
req, err := http.NewRequest(e.HTTPMethod, u.String(), strings.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "creating request")
|
||||
}
|
||||
|
||||
// remote addr
|
||||
req.RemoteAddr = e.RequestContext.Identity.SourceIP
|
||||
|
||||
// header fields
|
||||
for k, v := range e.Headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
// content-length
|
||||
if req.Header.Get("Content-Length") == "" && body != "" {
|
||||
req.Header.Set("Content-Length", strconv.Itoa(len(body)))
|
||||
}
|
||||
|
||||
// custom fields
|
||||
req.Header.Set("X-Request-Id", e.RequestContext.RequestID)
|
||||
req.Header.Set("X-Stage", e.RequestContext.Stage)
|
||||
|
||||
// xray support
|
||||
if traceID := ctx.Value("x-amzn-trace-id"); traceID != nil {
|
||||
req.Header.Set("X-Amzn-Trace-Id", fmt.Sprintf("%v", traceID))
|
||||
}
|
||||
|
||||
// host
|
||||
req.URL.Host = req.Header.Get("Host")
|
||||
req.Host = req.URL.Host
|
||||
|
||||
return req, nil
|
||||
}
|
||||
110
vendor/github.com/apex/gateway/response.go
generated
vendored
Normal file
110
vendor/github.com/apex/gateway/response.go
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
package gateway
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-lambda-go/events"
|
||||
)
|
||||
|
||||
// ResponseWriter implements the http.ResponseWriter interface
|
||||
// in order to support the API Gateway Lambda HTTP "protocol".
|
||||
type ResponseWriter struct {
|
||||
out events.APIGatewayProxyResponse
|
||||
buf bytes.Buffer
|
||||
header http.Header
|
||||
wroteHeader bool
|
||||
}
|
||||
|
||||
// NewResponse returns a new response writer to capture http output.
|
||||
func NewResponse() *ResponseWriter {
|
||||
return &ResponseWriter{}
|
||||
}
|
||||
|
||||
// Header implementation.
|
||||
func (w *ResponseWriter) Header() http.Header {
|
||||
if w.header == nil {
|
||||
w.header = make(http.Header)
|
||||
}
|
||||
|
||||
return w.header
|
||||
}
|
||||
|
||||
// Write implementation.
|
||||
func (w *ResponseWriter) Write(b []byte) (int, error) {
|
||||
if !w.wroteHeader {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
// TODO: HEAD? ignore
|
||||
|
||||
return w.buf.Write(b)
|
||||
}
|
||||
|
||||
// WriteHeader implementation.
|
||||
func (w *ResponseWriter) WriteHeader(status int) {
|
||||
if w.wroteHeader {
|
||||
return
|
||||
}
|
||||
|
||||
if w.Header().Get("Content-Type") == "" {
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf8")
|
||||
}
|
||||
|
||||
w.out.StatusCode = status
|
||||
|
||||
h := make(map[string]string)
|
||||
|
||||
for k, v := range w.Header() {
|
||||
if len(v) > 0 {
|
||||
h[k] = v[len(v)-1]
|
||||
}
|
||||
}
|
||||
|
||||
w.out.Headers = h
|
||||
w.wroteHeader = true
|
||||
}
|
||||
|
||||
// End the request.
|
||||
func (w *ResponseWriter) End() events.APIGatewayProxyResponse {
|
||||
w.out.IsBase64Encoded = isBinary(w.header)
|
||||
|
||||
if w.out.IsBase64Encoded {
|
||||
w.out.Body = base64.StdEncoding.EncodeToString(w.buf.Bytes())
|
||||
} else {
|
||||
w.out.Body = w.buf.String()
|
||||
}
|
||||
|
||||
return w.out
|
||||
}
|
||||
|
||||
// isBinary returns true if the response reprensents binary.
|
||||
func isBinary(h http.Header) bool {
|
||||
if !isTextMime(h.Get("Content-Type")) {
|
||||
return true
|
||||
}
|
||||
|
||||
if h.Get("Content-Encoding") == "gzip" {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// isTextMime returns true if the content type represents textual data.
|
||||
func isTextMime(kind string) bool {
|
||||
switch {
|
||||
case strings.HasSuffix(kind, "svg+xml"):
|
||||
return true
|
||||
case strings.HasPrefix(kind, "text/"):
|
||||
return true
|
||||
case strings.HasPrefix(kind, "application/") && strings.HasSuffix(kind, "json"):
|
||||
return true
|
||||
case strings.HasPrefix(kind, "application/") && strings.HasSuffix(kind, "xml"):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user