Skip to content

Commit 41aa2d5

Browse files
authored
fix panic when calling certain methods on non-client requests; fix HandoffFanout (#220)
1 parent 54e5d57 commit 41aa2d5

File tree

2 files changed

+112
-29
lines changed

2 files changed

+112
-29
lines changed

end_to_end_tests/loopback/main_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package main
55
import (
66
"context"
77
"io"
8+
"os"
89
"strings"
910
"testing"
1011

@@ -85,3 +86,74 @@ func Test1xxStatusCode(t *testing.T) {
8586
}
8687
}
8788
}
89+
90+
// Validate that accessors that (mostly) don't make sense against
91+
// backend requests return sane values.
92+
func TestBackendRequest(t *testing.T) {
93+
t.Run("FastlyMeta", func(t *testing.T) {
94+
req, err := fsthttp.NewRequest("GET", "http://anyplace.horse", nil)
95+
if err != nil {
96+
t.Fatal(err)
97+
}
98+
99+
// We should get a mostly-empty FastlyMeta for backend requests.
100+
meta, err := req.FastlyMeta()
101+
if err != nil {
102+
t.Fatalf("FastlyMeta: %v", err)
103+
}
104+
if meta == nil {
105+
t.Fatal("FastlyMeta() returned nil")
106+
}
107+
108+
if got, want := meta.SandboxID, os.Getenv("FASTLY_TRACE_ID"); got != want {
109+
t.Errorf("FastlyMeta.SandboxID = %q, want: %q", got, want)
110+
}
111+
if got, want := meta.RequestID, ""; got != want {
112+
t.Errorf("FastlyMeta.RequestID = %q, want: %q", got, want)
113+
}
114+
if got, want := meta.SandboxRequests, 0; got != want {
115+
t.Errorf("FastlyMeta.SandboxRequests = %d, want: %d", got, want)
116+
}
117+
})
118+
119+
t.Run("TLSClientCertificateInfo", func(t *testing.T) {
120+
req, err := fsthttp.NewRequest("GET", "http://anyplace.horse", nil)
121+
if err != nil {
122+
t.Fatal(err)
123+
}
124+
125+
ti, err := req.TLSClientCertificateInfo()
126+
if err != nil {
127+
t.Fatalf("TLSClientCertificateInfo: %v", err)
128+
}
129+
if ti == nil {
130+
t.Fatal("TLSClientCertificateInfo() returned nil")
131+
}
132+
133+
if got, want := len(ti.RawClientCertificate), 0; got != want {
134+
t.Errorf("len(TLSClientCertificateInfo.RawClientCertificate) = %d, want: %d", got, want)
135+
}
136+
})
137+
138+
t.Run("HandoffWebsocket", func(t *testing.T) {
139+
req, err := fsthttp.NewRequest("GET", "http://anyplace.horse", nil)
140+
if err != nil {
141+
t.Fatal(err)
142+
}
143+
144+
if got, want := req.HandoffWebsocket("self"), fsthttp.ErrHandoffNotSupported; got != want {
145+
t.Errorf("HandoffWebsocket() = %v, want: %v", got, want)
146+
}
147+
})
148+
149+
t.Run("HandoffFanout", func(t *testing.T) {
150+
req, err := fsthttp.NewRequest("GET", "http://anyplace.horse", nil)
151+
if err != nil {
152+
t.Fatal(err)
153+
}
154+
155+
if got, want := req.HandoffFanout("self"), fsthttp.ErrHandoffNotSupported; got != want {
156+
t.Errorf("HandoffFanout() = %v, want: %v", got, want)
157+
}
158+
})
159+
}

fsthttp/request.go

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -371,29 +371,31 @@ func (req *Request) FastlyMeta() (*FastlyMeta, error) {
371371
SandboxRequests: req.sandboxRequests,
372372
}
373373

374-
fastlyMeta.RequestID, err = req.downstream.req.DownstreamRequestID()
375-
if err != nil {
376-
return nil, fmt.Errorf("get request ID: %w", err)
377-
}
374+
if req.downstream.req != nil {
375+
fastlyMeta.RequestID, err = req.downstream.req.DownstreamRequestID()
376+
if err != nil {
377+
return nil, fmt.Errorf("get request ID: %w", err)
378+
}
378379

379-
fastlyMeta.H2, err = req.downstream.req.DownstreamH2Fingerprint()
380-
if err = ignoreNoneError(err); err != nil {
381-
return nil, fmt.Errorf("get H2 fingerprint: %w", err)
382-
}
380+
fastlyMeta.H2, err = req.downstream.req.DownstreamH2Fingerprint()
381+
if err = ignoreNoneError(err); err != nil {
382+
return nil, fmt.Errorf("get H2 fingerprint: %w", err)
383+
}
383384

384-
fastlyMeta.OH, err = req.downstream.req.DownstreamOHFingerprint()
385-
if err = ignoreNoneError(err); err != nil {
386-
return nil, fmt.Errorf("get OH fingerprint: %w", err)
387-
}
385+
fastlyMeta.OH, err = req.downstream.req.DownstreamOHFingerprint()
386+
if err = ignoreNoneError(err); err != nil {
387+
return nil, fmt.Errorf("get OH fingerprint: %w", err)
388+
}
388389

389-
fastlyMeta.DDOSDetected, err = req.downstream.req.DownstreamDDOSDetected()
390-
if err != nil {
391-
return nil, fmt.Errorf("get ddos detected: %w", err)
392-
}
390+
fastlyMeta.DDOSDetected, err = req.downstream.req.DownstreamDDOSDetected()
391+
if err != nil {
392+
return nil, fmt.Errorf("get ddos detected: %w", err)
393+
}
393394

394-
fastlyMeta.FastlyKeyIsValid, err = req.downstream.req.DownstreamFastlyKeyIsValid()
395-
if err != nil {
396-
return nil, fmt.Errorf("get fastly key is valid: %w", err)
395+
fastlyMeta.FastlyKeyIsValid, err = req.downstream.req.DownstreamFastlyKeyIsValid()
396+
if err != nil {
397+
return nil, fmt.Errorf("get fastly key is valid: %w", err)
398+
}
397399
}
398400

399401
req.fastlyMeta = fastlyMeta
@@ -563,7 +565,6 @@ func (req *Request) sendToImageOpto(ctx context.Context, backend string) (*Respo
563565
abiResp, abiBody, err := req.abi.req.SendToImageOpto(req.abi.body, backend, query)
564566
if err != nil {
565567
return nil, err
566-
567568
}
568569

569570
resp, err := newResponse(req, backend, abiResp, abiBody)
@@ -1053,15 +1054,17 @@ func (req *Request) TLSClientCertificateInfo() (*TLSClientCertificateInfo, error
10531054
var err error
10541055
var cert TLSClientCertificateInfo
10551056

1056-
cert.RawClientCertificate, err = req.downstream.req.DownstreamTLSRawClientCertificate()
1057-
if err = ignoreNoneError(err); err != nil {
1058-
return nil, fmt.Errorf("get TLS raw client certificate: %w", err)
1059-
}
1057+
if req.downstream.req != nil {
1058+
cert.RawClientCertificate, err = req.downstream.req.DownstreamTLSRawClientCertificate()
1059+
if err = ignoreNoneError(err); err != nil {
1060+
return nil, fmt.Errorf("get TLS raw client certificate: %w", err)
1061+
}
10601062

1061-
if cert.RawClientCertificate != nil {
1062-
cert.ClientCertIsVerified, err = req.downstream.req.DownstreamTLSClientCertVerifyResult()
1063-
if err != nil {
1064-
return nil, fmt.Errorf("get TLS client certificate verify: %w", err)
1063+
if cert.RawClientCertificate != nil {
1064+
cert.ClientCertIsVerified, err = req.downstream.req.DownstreamTLSClientCertVerifyResult()
1065+
if err != nil {
1066+
return nil, fmt.Errorf("get TLS client certificate verify: %w", err)
1067+
}
10651068
}
10661069
}
10671070

@@ -1115,6 +1118,8 @@ type DecompressResponseOptions struct {
11151118
Gzip bool
11161119
}
11171120

1121+
var ErrHandoffNotSupported = errors.New("handoff not supported on this request")
1122+
11181123
// HandoffWebsocket passes the WebSocket directly to a backend.
11191124
//
11201125
// This can only be used on services that have the WebSockets feature
@@ -1124,6 +1129,9 @@ type DecompressResponseOptions struct {
11241129
// Once this method has been called, no other response can be sent to this
11251130
// request, and the application can exit without affecting the send.
11261131
func (r *Request) HandoffWebsocket(backend string) error {
1132+
if r.downstream.req == nil {
1133+
return ErrHandoffNotSupported
1134+
}
11271135
return r.downstream.req.HandoffWebsocket(backend)
11281136
}
11291137

@@ -1136,7 +1144,10 @@ func (r *Request) HandoffWebsocket(backend string) error {
11361144
// called, no other response can be sent to this request, and the
11371145
// application can exit without affecting the send.
11381146
func (r *Request) HandoffFanout(backend string) error {
1139-
return r.downstream.req.HandoffWebsocket(backend)
1147+
if r.downstream.req == nil {
1148+
return ErrHandoffNotSupported
1149+
}
1150+
return r.downstream.req.HandoffFanout(backend)
11401151
}
11411152

11421153
// nopCloser is functionally the same as io.NopCloser, except that we

0 commit comments

Comments
 (0)