blob: e70b4cad4d8d23f651a18cc72e12ef49986a7ae8 [file] [log] [blame]
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package internal
import (
"context"
"net/http"
"sync/atomic"
"time"
)
// RequestInfo is information about an HTTP request.
type RequestInfo struct {
Request *http.Request
TrimmedURL string // URL with the scheme and host removed
TraceID string // extracted from request header
Start time.Time // when the request began
State atomic.Value // string describing current state; see [RequestState]
Cancel func(error) // function that cancels the request's context
}
func NewRequestInfo(r *http.Request) *RequestInfo {
turl := *r.URL
turl.Scheme = ""
turl.Host = ""
turl.User = nil
ri := &RequestInfo{
Request: r,
TrimmedURL: turl.String(),
TraceID: r.Header.Get("X-Cloud-Trace-Context"),
Start: time.Now(),
}
ri.State.Store("")
return ri
}
// requestInfoKey is the type of the context key for RequestInfos.
type requestInfoKey struct{}
// NewContextWithRequestInfo creates a new context from ctx that adds the trace ID.
func NewContextWithRequestInfo(ctx context.Context, ri *RequestInfo) context.Context {
return context.WithValue(ctx, requestInfoKey{}, ri)
}
// RequestInfoFromContext retrieves the trace ID from the context, or a zero one
// if it isn't there.
func RequestInfoFromContext(ctx context.Context) *RequestInfo {
ri, _ := ctx.Value(requestInfoKey{}).(*RequestInfo)
if ri == nil {
return &RequestInfo{}
}
return ri
}
// RequestState sets the State field of the current request.
// It returns a function that restores the value of the field.
// Typical use:
//
// defer RequestState(ctx, "frobbing")()
func RequestState(ctx context.Context, s string) func() {
ri := RequestInfoFromContext(ctx)
old := ri.State.Load()
if old == nil {
// This RequestInfo was not initialized with NewRequestInfo.
// It is the zero value returned by RequestInfoFromContext
// when there is no RequestInfo in the context. Do nothing in this case.
return func() {}
}
ri.State.Store(s)
return func() { ri.State.Store(old) }
}