Skip to content

Commit 3eac1c6

Browse files
authored
Merge pull request #181 from fastly/dgryski/http-downstream
Add more downstream hostcalls
2 parents 080630c + 01ded73 commit 3eac1c6

File tree

4 files changed

+441
-67
lines changed

4 files changed

+441
-67
lines changed

_examples/reusable-sessions/main.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package main
55
import (
66
"context"
77
"fmt"
8+
"os"
89
"time"
910

1011
"github.com/fastly/compute-sdk-go/fsthttp"
@@ -14,10 +15,10 @@ func main() {
1415
var requests int
1516
fsthttp.ServeMany(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
1617
requests++
17-
fmt.Fprintf(w, "Request %v, Hello, %s!\n", requests, r.RemoteAddr)
18+
fmt.Fprintf(w, "Request %v, Hello, %s (%q, %q)!\n", requests, r.RemoteAddr, os.Getenv("FASTLY_TRACE_ID"), r.RequestID)
1819
}, &fsthttp.ServeManyOptions{
19-
NextTimeout: 10 * time.Second,
20+
NextTimeout: 1 * time.Second,
2021
MaxRequests: 100,
21-
MaxLifetime: 10 * time.Second,
22+
MaxLifetime: 5 * time.Second,
2223
})
2324
}

fsthttp/request.go

Lines changed: 119 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ type Request struct {
7676
// TLSInfo collects TLS metadata for incoming requests received over HTTPS.
7777
TLSInfo TLSInfo
7878

79+
// tlsClientCertificateInfo is information about the tls client certificate, if available
80+
clientCertificate *TLSClientCertificateInfo
81+
82+
// FastlyMeta collects Fastly-specific metadata for incoming requests
83+
fastlyMeta *FastlyMeta
84+
7985
// SendPollInterval determines how often the Send method will check for
8086
// completed requests. While polling, the Go runtime is suspended, and all
8187
// user code stops execution. A shorter interval will make Send more
@@ -106,12 +112,17 @@ type Request struct {
106112
// discarded.
107113
ManualFramingMode bool
108114

115+
// RequestID is the current Fastly request ID
116+
RequestID string
117+
109118
sent bool // a request may only be sent once
110119

111-
abi struct {
112-
req *fastly.HTTPRequest
113-
body *fastly.HTTPBody
114-
}
120+
abi reqAbi
121+
}
122+
123+
type reqAbi struct {
124+
req *fastly.HTTPRequest
125+
body *fastly.HTTPBody
115126
}
116127

117128
// NewRequest constructs an outgoing request with the given HTTP method, URI,
@@ -160,6 +171,11 @@ func newClientRequest(abiReq *fastly.HTTPRequest, abiReqBody *fastly.HTTPBody) (
160171
return nil, fmt.Errorf("get protocol version: %w", err)
161172
}
162173

174+
reqID, err := abiReq.DownstreamRequestID()
175+
if err != nil {
176+
return nil, fmt.Errorf("get request id: %w", err)
177+
}
178+
163179
header := NewHeader()
164180
keys := abiReq.GetHeaderNames()
165181
for keys.Next() {
@@ -209,6 +225,12 @@ func newClientRequest(abiReq *fastly.HTTPRequest, abiReqBody *fastly.HTTPBody) (
209225
if err != nil {
210226
return nil, fmt.Errorf("get TLS JA3 MD5: %w", err)
211227
}
228+
229+
tlsInfo.JA4, err = abiReq.DownstreamTLSJA4()
230+
if err != nil {
231+
return nil, fmt.Errorf("get TLS JA4: %w", err)
232+
}
233+
212234
}
213235

214236
// Setting the fsthttp.Request Host field to the url.URL Host field is
@@ -226,6 +248,8 @@ func newClientRequest(abiReq *fastly.HTTPRequest, abiReqBody *fastly.HTTPBody) (
226248
RemoteAddr: remoteAddr.String(),
227249
ServerAddr: serverAddr.String(),
228250
TLSInfo: tlsInfo,
251+
RequestID: reqID,
252+
abi: reqAbi{req: abiReq, body: abiReqBody},
229253
}, nil
230254
}
231255

@@ -337,6 +361,44 @@ func (req *Request) AddCookie(c *Cookie) {
337361
}
338362
}
339363

364+
// FastlyMeta returns a fleshed-out FastlyMeta object for the request.
365+
func (req *Request) FastlyMeta() (*FastlyMeta, error) {
366+
if req.fastlyMeta != nil {
367+
return req.fastlyMeta, nil
368+
}
369+
370+
var err error
371+
372+
var fastlyMeta FastlyMeta
373+
fastlyMeta.H2, err = req.abi.req.DownstreamH2Fingerprint()
374+
if err != nil {
375+
if status, ok := fastly.IsFastlyError(err); ok && status != fastly.FastlyStatusNone {
376+
return nil, fmt.Errorf("get H2 fingerprint: %w", err)
377+
}
378+
}
379+
380+
fastlyMeta.OH, err = req.abi.req.DownstreamOHFingerprint()
381+
if err != nil {
382+
if status, ok := fastly.IsFastlyError(err); ok && status != fastly.FastlyStatusNone {
383+
return nil, fmt.Errorf("get OH fingerprint: %w", err)
384+
}
385+
}
386+
387+
fastlyMeta.DDOSDetected, err = req.abi.req.DownstreamDDOSDetected()
388+
if err != nil {
389+
return nil, fmt.Errorf("get ddos detected: %w", err)
390+
}
391+
392+
fastlyMeta.FastlyKeyIsValid, err = req.abi.req.DownstreamFastlyKeyIsValid()
393+
if err != nil {
394+
return nil, fmt.Errorf("get fastly key is valid: %w", err)
395+
}
396+
397+
req.fastlyMeta = &fastlyMeta
398+
399+
return req.fastlyMeta, nil
400+
}
401+
340402
// Send the request to the named backend. Requests may only be sent to
341403
// backends that have been preconfigured in your service, regardless of
342404
// their URL. Once sent, a request cannot be sent again.
@@ -940,6 +1002,59 @@ type TLSInfo struct {
9401002
// JA3MD5 contains the bytes of the JA3 signature of the client TLS request.
9411003
// See https://www.fastly.com/blog/the-state-of-tls-fingerprinting-whats-working-what-isnt-and-whats-next
9421004
JA3MD5 []byte
1005+
1006+
// JA4 contains the bytes of the JA4 signature of the client TLS request.
1007+
// See https://github.com/FoxIO-LLC/ja4/blob/main/technical_details/JA4.md
1008+
JA4 []byte
1009+
}
1010+
1011+
func (req *Request) TLSClientCertificateInfo() (*TLSClientCertificateInfo, error) {
1012+
if req.clientCertificate != nil {
1013+
return req.clientCertificate, nil
1014+
}
1015+
1016+
var err error
1017+
var cert TLSClientCertificateInfo
1018+
1019+
cert.RawClientCertificate, err = req.abi.req.DownstreamTLSRawClientCertificate()
1020+
if err != nil {
1021+
return nil, fmt.Errorf("get TLS raw client certificate: %w", err)
1022+
}
1023+
1024+
if cert.RawClientCertificate != nil {
1025+
cert.ClientCertIsVerified, err = req.abi.req.DownstreamTLSClientCertVerifyResult()
1026+
if err != nil {
1027+
return nil, fmt.Errorf("get TLS client certificate verify: %w", err)
1028+
}
1029+
}
1030+
1031+
req.clientCertificate = &cert
1032+
return req.clientCertificate, nil
1033+
}
1034+
1035+
type TLSClientCertificateInfo struct {
1036+
// RawClientCertificate contains the bytes of the raw client certificate, if one was provided.
1037+
RawClientCertificate []byte
1038+
1039+
// ClientCertIsVerified is true if the provided client certificate is valid.
1040+
ClientCertIsVerified bool
1041+
}
1042+
1043+
// FastlyMeta holds various Fastly-specific metadata for a request.
1044+
type FastlyMeta struct {
1045+
1046+
// H2 is the HTTP/2 fingerprint of a client request if available
1047+
H2 []byte
1048+
1049+
// OH is a fingerprint of the client request's original headers
1050+
OH []byte
1051+
1052+
// DDOSDetected is true if the request was determined to be part of a DDOS attack.
1053+
DDOSDetected bool
1054+
1055+
// FastlyKeyIsValid is true if the request contains a valid Fastly API token.
1056+
// This is for services to restrict authenticating PURGE requests for the readthrough cache.
1057+
FastlyKeyIsValid bool
9431058
}
9441059

9451060
// DecompressResponseOptions control the auto decompress response behaviour.

internal/abi/fastly/hostcalls_noguest.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,42 @@ func (r *HTTPRequest) DownstreamTLSJA3MD5() ([]byte, error) {
9191
return nil, fmt.Errorf("not implemented")
9292
}
9393

94+
func (r *HTTPRequest) DownstreamH2Fingerprint() ([]byte, error) {
95+
return nil, fmt.Errorf("not implemented")
96+
}
97+
98+
func (r *HTTPRequest) DownstreamRequestID() (string, error) {
99+
return "", fmt.Errorf("not implemented")
100+
}
101+
102+
func (r *HTTPRequest) DownstreamOHFingerprint() ([]byte, error) {
103+
return nil, fmt.Errorf("not implemented")
104+
}
105+
106+
func (r *HTTPRequest) DownstreamDDOSDetected() (bool, error) {
107+
return false, fmt.Errorf("not implemented")
108+
}
109+
110+
func (r *HTTPRequest) DownstreamTLSRawClientCertificate() ([]byte, error) {
111+
return nil, fmt.Errorf("not implemented")
112+
}
113+
114+
func (r *HTTPRequest) DownstreamTLSClientCertVerifyResult() (bool, error) {
115+
return false, fmt.Errorf("not implemented")
116+
}
117+
118+
func (r *HTTPRequest) DownstreamTLSJA4() ([]byte, error) {
119+
return nil, fmt.Errorf("not implemented")
120+
}
121+
122+
func (r *HTTPRequest) DownstreamComplianceRegion() (string, error) {
123+
return "", fmt.Errorf("not implemented")
124+
}
125+
126+
func (r *HTTPRequest) DownstreamFastlyKeyIsValid() (bool, error) {
127+
return false, fmt.Errorf("not implemented")
128+
}
129+
94130
func NewHTTPRequest() (*HTTPRequest, error) {
95131
return nil, fmt.Errorf("not implemented")
96132
}

0 commit comments

Comments
 (0)