feat(aws): support lambda (#334)

This commit is contained in:
Bo-Yi Wu
2018-01-23 16:34:34 +08:00
committed by GitHub
parent b0260af17b
commit d7ce3c077c
43 changed files with 2795 additions and 2 deletions

34
vendor/github.com/apex/gateway/Readme.md generated vendored Normal file
View 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")
}
```
---
[![GoDoc](https://godoc.org/github.com/apex/up-go?status.svg)](https://godoc.org/github.com/apex/gateway)
![](https://img.shields.io/badge/license-MIT-blue.svg)
![](https://img.shields.io/badge/status-stable-green.svg)
<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
View 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
View 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
View 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
}
}