Skip to content

Commit 59c4850

Browse files
committed
Merge branch 'master' into f-add-support-for-function-url
# Conflicts: # go.mod # go.sum
2 parents ed10048 + 55b7779 commit 59c4850

File tree

10 files changed

+408
-624
lines changed

10 files changed

+408
-624
lines changed

chi/adapter.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Packge chilambda add Chi support for the aws-severless-go-api library.
1+
// Package chiadapter add Chi support for the aws-severless-go-api library.
22
// Uses the core package behind the scenes and exposes the New method to
33
// get a new instance and Proxy method to send request to the Chi mux.
44
package chiadapter

chi/adapterv2.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package chiadapter
2+
3+
import (
4+
"context"
5+
"net/http"
6+
7+
"github.com/aws/aws-lambda-go/events"
8+
"github.com/awslabs/aws-lambda-go-api-proxy/core"
9+
"github.com/go-chi/chi/v5"
10+
)
11+
12+
// ChiLambdaV2 makes it easy to send API Gateway proxy V2 events to a Chi
13+
// Mux. The library transforms the proxy event into an HTTP request and then
14+
// creates a proxy response object from the http.ResponseWriter
15+
type ChiLambdaV2 struct {
16+
core.RequestAccessorV2
17+
18+
chiMux *chi.Mux
19+
}
20+
21+
// New creates a new instance of the ChiLambdaV2 object.
22+
// Receives an initialized *chi.Mux object - normally created with chi.NewRouter().
23+
// It returns the initialized instance of the ChiLambdaV2 object.
24+
func NewV2(chi *chi.Mux) *ChiLambdaV2 {
25+
return &ChiLambdaV2{chiMux: chi}
26+
}
27+
28+
// Proxy receives an API Gateway proxy V2 event, transforms it into an http.Request
29+
// object, and sends it to the chi.Mux for routing.
30+
// It returns a proxy response object generated from the http.ResponseWriter.
31+
func (g *ChiLambdaV2) Proxy(req events.APIGatewayV2HTTPRequest) (events.APIGatewayV2HTTPResponse, error) {
32+
chiRequest, err := g.ProxyEventToHTTPRequest(req)
33+
return g.proxyInternal(chiRequest, err)
34+
}
35+
36+
// ProxyWithContext receives context and an API Gateway proxy V2 event,
37+
// transforms them into an http.Request object, and sends it to the chi.Mux for routing.
38+
// It returns a proxy response object generated from the http.ResponseWriter.
39+
func (g *ChiLambdaV2) ProxyWithContextV2(ctx context.Context, req events.APIGatewayV2HTTPRequest) (events.APIGatewayV2HTTPResponse, error) {
40+
chiRequest, err := g.EventToRequestWithContext(ctx, req)
41+
return g.proxyInternal(chiRequest, err)
42+
}
43+
44+
func (g *ChiLambdaV2) proxyInternal(chiRequest *http.Request, err error) (events.APIGatewayV2HTTPResponse, error) {
45+
46+
if err != nil {
47+
return core.GatewayTimeoutV2(), core.NewLoggedError("Could not convert proxy event to request: %v", err)
48+
}
49+
50+
respWriter := core.NewProxyResponseWriterV2()
51+
g.chiMux.ServeHTTP(http.ResponseWriter(respWriter), chiRequest)
52+
53+
proxyResponse, err := respWriter.GetProxyResponse()
54+
if err != nil {
55+
return core.GatewayTimeoutV2(), core.NewLoggedError("Error while generating proxy response: %v", err)
56+
}
57+
58+
return proxyResponse, nil
59+
}

chi/chilambda_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,36 @@ var _ = Describe("ChiLambda tests", func() {
4141
})
4242
})
4343
})
44+
45+
var _ = Describe("ChiLambdaV2 tests", func() {
46+
Context("Simple ping request", func() {
47+
It("Proxies the event correctly", func() {
48+
log.Println("Starting test")
49+
50+
r := chi.NewRouter()
51+
r.Get("/ping", func(w http.ResponseWriter, r *http.Request) {
52+
w.Write([]byte("pong"))
53+
})
54+
55+
adapter := chiadapter.NewV2(r)
56+
57+
req := events.APIGatewayV2HTTPRequest{
58+
RequestContext: events.APIGatewayV2HTTPRequestContext{
59+
HTTP: events.APIGatewayV2HTTPRequestContextHTTPDescription{
60+
Method: "GET",
61+
Path: "/ping",
62+
},
63+
},
64+
}
65+
66+
resp, err := adapter.ProxyWithContextV2(context.Background(), req)
67+
68+
Expect(err).To(BeNil())
69+
Expect(resp.StatusCode).To(Equal(200))
70+
71+
resp, err = adapter.Proxy(req)
72+
Expect(err).To(BeNil())
73+
Expect(resp.StatusCode).To(Equal(200))
74+
})
75+
})
76+
})

core/requestv2.go

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"fmt"
1212
"log"
1313
"net/http"
14+
"net/textproto"
1415
"net/url"
1516
"os"
1617
"strings"
@@ -170,7 +171,13 @@ func (r *RequestAccessorV2) EventToRequest(req events.APIGatewayV2HTTPRequest) (
170171
httpRequest.Header.Add("Cookie", cookie)
171172
}
172173

173-
for headerKey, headerValue := range req.Headers {
174+
singletonHeaders, headers := splitSingletonHeaders(req.Headers)
175+
176+
for headerKey, headerValue := range singletonHeaders {
177+
httpRequest.Header.Add(headerKey, headerValue)
178+
}
179+
180+
for headerKey, headerValue := range headers {
174181
for _, val := range strings.Split(headerValue, ",") {
175182
httpRequest.Header.Add(headerKey, strings.Trim(val, " "))
176183
}
@@ -227,3 +234,38 @@ type requestContextV2 struct {
227234
gatewayProxyContext events.APIGatewayV2HTTPRequestContext
228235
stageVars map[string]string
229236
}
237+
238+
// splitSingletonHeaders splits the headers into single-value headers and other,
239+
// multi-value capable, headers.
240+
// Returns (single-value headers, multi-value-capable headers)
241+
func splitSingletonHeaders(headers map[string]string) (map[string]string, map[string]string) {
242+
singletons := make(map[string]string)
243+
multitons := make(map[string]string)
244+
for headerKey, headerValue := range headers {
245+
if ok := singletonHeaders[textproto.CanonicalMIMEHeaderKey(headerKey)]; ok {
246+
singletons[headerKey] = headerValue
247+
} else {
248+
multitons[headerKey] = headerValue
249+
}
250+
}
251+
252+
return singletons, multitons
253+
}
254+
255+
// singletonHeaders is a set of headers, that only accept a single
256+
// value which may be comma separated (according to RFC 7230)
257+
var singletonHeaders = map[string]bool{
258+
"Content-Type": true,
259+
"Content-Disposition": true,
260+
"Content-Length": true,
261+
"User-Agent": true,
262+
"Referer": true,
263+
"Host": true,
264+
"Authorization": true,
265+
"Proxy-Authorization": true,
266+
"If-Modified-Since": true,
267+
"If-Unmodified-Since": true,
268+
"From": true,
269+
"Location": true,
270+
"Max-Forwards": true,
271+
}

core/requestv2_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,35 @@ var _ = Describe("RequestAccessorV2 tests", func() {
136136
}
137137
})
138138

139+
singletonHeaderRequest := getProxyRequestV2("/hello", "GET")
140+
singletonHeaderRequest.Headers = map[string]string{
141+
// multi-value capable headers
142+
"hello": "1",
143+
"world": "2,3",
144+
// singleton headers, which may be comma separated
145+
"user-agent": "Mozilla/5.0 (Linux; Android 11; Pixel 5 Build/RQ3A.210805.001.A1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.159 Mobile Safari/537.36",
146+
"authorization": "some custom comma, separated authorization",
147+
}
148+
149+
It("Populates singleton header values correctly", func() {
150+
httpReq, err := accessor.EventToRequestWithContext(context.Background(), singletonHeaderRequest)
151+
Expect(err).To(BeNil())
152+
Expect("/hello").To(Equal(httpReq.URL.Path))
153+
Expect("GET").To(Equal(httpReq.Method))
154+
155+
headers := httpReq.Header
156+
Expect(4).To(Equal(len(headers)))
157+
158+
for k, value := range headers {
159+
k = strings.ToLower(k)
160+
if k == "hello" || k == "world" {
161+
Expect(strings.Join(value, ",")).To(Equal(singletonHeaderRequest.Headers[k]))
162+
} else {
163+
Expect(headers.Get(k)).To(Equal(singletonHeaderRequest.Headers[k]))
164+
}
165+
}
166+
})
167+
139168
svhRequest := getProxyRequestV2("/hello", "GET")
140169
svhRequest.Headers = map[string]string{
141170
"hello": "1",

core/response.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ func (r *ProxyResponseWriter) WriteHeader(status int) {
8282
r.status = status
8383
}
8484

85+
// Flush implements the Flusher interface which is called by
86+
// some implementers. This is intentionally a no-op
87+
func (r *ProxyResponseWriter) Flush() {
88+
//no-op
89+
}
90+
8591
// GetProxyResponse converts the data passed to the response writer into
8692
// an events.APIGatewayProxyResponse object.
8793
// Returns a populated proxy response object. If the response is invalid, for example

fiber/adapter.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ package fiberadapter
55

66
import (
77
"context"
8-
"io/ioutil"
8+
"fmt"
9+
"io"
10+
"log"
911
"net"
1012
"net/http"
13+
"strings"
1114

1215
"github.com/aws/aws-lambda-go/events"
1316
"github.com/gofiber/fiber/v2"
@@ -103,7 +106,7 @@ func (f *FiberLambda) adaptor(w http.ResponseWriter, r *http.Request) {
103106
defer fasthttp.ReleaseRequest(req)
104107

105108
// Convert net/http -> fasthttp request
106-
body, err := ioutil.ReadAll(r.Body)
109+
body, err := io.ReadAll(r.Body)
107110
if err != nil {
108111
http.Error(w, utils.StatusMessage(fiber.StatusInternalServerError), fiber.StatusInternalServerError)
109112
return
@@ -129,8 +132,16 @@ func (f *FiberLambda) adaptor(w http.ResponseWriter, r *http.Request) {
129132
}
130133
}
131134

132-
remoteAddr, err := net.ResolveTCPAddr("tcp", r.RemoteAddr)
135+
// We need to make sure the net.ResolveTCPAddr call works as it expects a port
136+
addrWithPort := r.RemoteAddr
137+
if !strings.Contains(r.RemoteAddr, ":") {
138+
addrWithPort = r.RemoteAddr + ":80" // assuming a default port
139+
}
140+
141+
remoteAddr, err := net.ResolveTCPAddr("tcp", addrWithPort)
133142
if err != nil {
143+
fmt.Printf("could not resolve TCP address for addr %s\n", r.RemoteAddr)
144+
log.Println(err)
134145
http.Error(w, utils.StatusMessage(fiber.StatusInternalServerError), fiber.StatusInternalServerError)
135146
return
136147
}

fiber/fiberlambda_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,39 @@ var _ = Describe("FiberLambda tests", func() {
3939
})
4040
})
4141

42+
Context("RemoteAddr handling", func() {
43+
It("Properly parses the IP address", func() {
44+
app := fiber.New()
45+
app.Get("/ping", func(c *fiber.Ctx) error {
46+
// make sure the ip address is actually set properly
47+
Expect(c.Context().RemoteAddr().String()).To(Equal("8.8.8.8:80"))
48+
return c.SendString("pong")
49+
})
50+
51+
adapter := fiberadaptor.New(app)
52+
53+
req := events.APIGatewayProxyRequest{
54+
Path: "/ping",
55+
HTTPMethod: "GET",
56+
RequestContext: events.APIGatewayProxyRequestContext{
57+
Identity: events.APIGatewayRequestIdentity{
58+
SourceIP: "8.8.8.8",
59+
},
60+
},
61+
}
62+
63+
resp, err := adapter.ProxyWithContext(context.Background(), req)
64+
65+
Expect(err).To(BeNil())
66+
Expect(resp.StatusCode).To(Equal(200))
67+
68+
resp, err = adapter.Proxy(req)
69+
70+
Expect(err).To(BeNil())
71+
Expect(resp.StatusCode).To(Equal(200))
72+
})
73+
})
74+
4275
Context("Request header", func() {
4376
It("Check pass canonical header to fiber", func() {
4477
app := fiber.New()

sample/go.mod

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,12 @@ module github.com/awslabs/aws-lambda-go-api-proxy-sample
33
go 1.12
44

55
require (
6-
github.com/aws/aws-lambda-go v1.37.0
7-
github.com/awslabs/aws-lambda-go-api-proxy v0.14.0
8-
github.com/bytedance/sonic v1.8.2 // indirect
9-
github.com/gin-gonic/gin v1.9.0
6+
github.com/aws/aws-lambda-go v1.41.0
7+
github.com/awslabs/aws-lambda-go-api-proxy v0.16.0
8+
github.com/gin-gonic/gin v1.9.1
109
github.com/google/uuid v1.3.0
11-
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
12-
github.com/leodido/go-urn v1.2.2 // indirect
13-
github.com/onsi/gomega v1.19.0 // indirect
14-
github.com/ugorji/go/codec v1.2.10 // indirect
15-
golang.org/x/arch v0.2.0 // indirect
16-
golang.org/x/crypto v0.6.0 // indirect
10+
github.com/kr/pretty v0.3.0 // indirect
11+
github.com/rogpeppe/go-internal v1.8.0 // indirect
1712
)
1813

1914
replace (

0 commit comments

Comments
 (0)