/* * Copyright 2020 The Dragonfly Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package source import ( "context" "errors" "net/url" ) type Request struct { URL *url.URL Header Header // ctx is either the client or server context. It should only // be modified via copying the whole Request using WithContext. // It is unexported to prevent people from using Context wrong // and mutating the contexts held by callers of the same request. ctx context.Context } func NewRequest(rawURL string) (*Request, error) { u, err := url.Parse(rawURL) if err != nil { return nil, err } return &Request{ URL: u, Header: make(Header), ctx: context.Background(), }, nil } func NewRequestWithHeader(rawURL string, header map[string]string) (*Request, error) { request, err := NewRequest(rawURL) if err != nil { return nil, err } for k, v := range header { request.Header.Add(k, v) } return request, nil } func NewRequestWithContext(ctx context.Context, rawURL string, header map[string]string) (*Request, error) { if ctx == nil { return nil, errors.New("nil Context") } u, err := url.Parse(rawURL) if err != nil { return nil, err } req := &Request{ ctx: ctx, URL: u, Header: make(Header), } for k, v := range header { req.Header.Add(k, v) } return req, nil } // Context returns the request's context. To change the context, use // WithContext. // // The returned context is always non-nil; it defaults to the // background context. // // For outgoing client requests, the context controls cancellation. // // For incoming server requests, the context is canceled when the // client's connection closes, the request is canceled (with HTTP/2), // or when the ServeHTTP method returns. func (r *Request) Context() context.Context { if r.ctx != nil { return r.ctx } return context.Background() } // WithContext returns a shallow copy of r with its context changed // to ctx. The provided ctx must be non-nil. // // For outgoing client request, the context controls the entire // lifetime of a request and its response: obtaining a connection, // sending the request, and reading the response headers and body. // // To create a new request with a context, use NewRequestWithContext. // To change the context of a request, such as an incoming request you // want to modify before sending back out, use Request.Clone. Between // those two uses, it's rare to need WithContext. func (r *Request) WithContext(ctx context.Context) *Request { if ctx == nil { panic("nil context") } r2 := new(Request) *r2 = *r r2.ctx = ctx r2.URL = cloneURL(r.URL) // legacy behavior; TODO: try to remove. Issue 23544 return r2 } func cloneURL(u *url.URL) *url.URL { if u == nil { return nil } u2 := new(url.URL) *u2 = *u if u.User != nil { u2.User = new(url.Userinfo) *u2.User = *u.User } return u2 } // Clone returns a deep copy of r with its context changed to ctx. // The provided ctx must be non-nil. // // For an outgoing client request, the context controls the entire // lifetime of a request and its response: obtaining a connection, // sending the request, and reading the response headers and body. func (r *Request) Clone(ctx context.Context) *Request { if ctx == nil { panic("nil context") } r2 := new(Request) *r2 = *r r2.ctx = ctx r2.URL = cloneURL(r.URL) if r.Header != nil { r2.Header = r.Header.Clone() } return r2 }