You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
go-library/vendor/gopkg.in/h2non/gentleman.v2/context/context.go

348 lines
9.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// Package context implements a request-aware HTTP context used by plugins
// and exposed by the middleware layer, designed to share polymorfic data
// types across plugins in the middleware call chain.
//
// It is built on top of the standard built-in context package in Go:
// https://golang.org/pkg/context
//
// gentleman's Context implements the stdlib `context.Context` interface:
// https://golang.org/pkg/context/#Context
package context
import (
"context"
"net/http"
"net/url"
"time"
"gopkg.in/h2non/gentleman.v2/utils"
)
// Key stores the key identifier for the built-in context
var Key interface{} = "$gentleman"
// Store represents the map store for context store.
type Store map[interface{}]interface{}
// Context encapsulates required domain-specific HTTP entities
// to share data and entities for HTTP transactions in a middleware chain
type Context struct {
// Stores the last error for the current Context
Error error
// Flag if the HTTP transaction was explicitly stopped in some phase
Stopped bool
// Context can inherit behavior and data from another Context
Parent *Context
// Reference to the http.Client used in the current HTTP transaction
Client *http.Client
// Reference to the http.Request used in the current HTTP transaction
Request *http.Request
// Reference to the http.Response used in the current HTTP transaction
Response *http.Response
}
// New creates an empty default Context
func New() *Context {
req := createRequest()
res := createResponse(req)
cli := &http.Client{Transport: http.DefaultTransport}
return &Context{Request: req, Response: res, Client: cli}
}
// getStore retrieves the current request context data store.
func (c *Context) getStore() Store {
store, ok := c.Request.Context().Value(Key).(Store)
if !ok {
panic("invalid request context")
}
return store
}
// Set sets a value on the current store
func (c *Context) Set(key interface{}, value interface{}) {
store := c.getStore()
store[key] = value
}
// Get gets a value by key in the current or parent context
func (c *Context) Get(key interface{}) interface{} {
store := c.getStore()
if store == nil {
return store
}
if value, ok := store[key]; ok {
return value
}
if c.Parent != nil {
return c.Parent.Get(key)
}
return nil
}
// GetOk gets a context value from req.
// Returns (nil, false) if key not found in the request context.
func (c *Context) GetOk(key interface{}) (interface{}, bool) {
store := c.getStore()
val, ok := store[key]
if !ok {
if c.Parent != nil {
return c.Parent.GetOk(key)
}
}
return val, ok
}
// GetInt gets an int context value from req.
// Returns an empty string if key not found in the request context,
// or the value does not evaluate to a string
func (c *Context) GetInt(key interface{}) (int, bool) {
value, ok := c.GetOk(key)
if !ok {
if c.Parent != nil {
return c.Parent.GetInt(key)
}
}
if num, ok := value.(int); ok {
return num, ok
}
return 0, false
}
// GetString gets a string context value from req.
// Returns an empty string if key not found in the request context,
// or the value does not evaluate to a string
func (c *Context) GetString(key interface{}) string {
store := c.getStore()
if value, ok := store[key]; ok {
if typed, ok := value.(string); ok {
return typed
}
}
if c.Parent != nil {
return c.Parent.GetString(key)
}
return ""
}
// GetAll returns all stored context values for a request.
// Will always return a valid map. Returns an empty map for
// requests context data previously set
func (c *Context) GetAll() Store {
buf := Store{}
for key, value := range c.getStore() {
buf[key] = value
}
if c.Parent != nil {
for key, value := range c.Parent.GetAll() {
buf[key] = value
}
}
return buf
}
// Delete deletes a stored value from a requests context
func (c *Context) Delete(key interface{}) {
delete(c.getStore(), key)
}
// Clear clears all stored values in the current requests context.
// Parent context store will not be cleaned.
func (c *Context) Clear() {
store := c.getStore()
for key := range store {
delete(store, key)
}
}
// UseParent uses a new parent Context
func (c *Context) UseParent(ctx *Context) {
c.Parent = ctx
}
// Root returns the root Context looking in the parent contexts recursively.
// If the current context has no parent context, it will return the Context itself.
func (c *Context) Root() *Context {
if c.Parent != nil {
return c.Parent.Root()
}
return c
}
// SetRequest replaces the context http.Request
func (c *Context) SetRequest(req *http.Request) {
c.Request = req.WithContext(c.Request.Context())
}
// Clone returns a clone of the current context.
func (c *Context) Clone() *Context {
ctx := new(Context)
*ctx = *c
req := new(http.Request)
*req = *c.Request
ctx.Request = req
c.CopyTo(ctx)
res := new(http.Response)
*res = *c.Response
ctx.Response = res
return ctx
}
// CopyTo copies the current context store into a new Context.
func (c *Context) CopyTo(newCtx *Context) {
store := Store{}
for key, value := range c.getStore() {
store[key] = value
}
ctx := context.WithValue(context.Background(), Key, store)
newCtx.Request = newCtx.Request.WithContext(ctx)
}
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
func (c *Context) Deadline() (deadline time.Time, ok bool) {
return c.Request.Context().Deadline()
}
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
//
// WithCancel arranges for Done to be closed when cancel is called;
// WithDeadline arranges for Done to be closed when the deadline
// expires; WithTimeout arranges for Done to be closed when the timeout
// elapses.
//
// Done is provided for use in select statements:
//
// // Stream generates values with DoSomething and sends them to out
// // until DoSomething returns an error or ctx.Done is closed.
// func Stream(ctx context.Context, out chan<- Value) error {
// for {
// v, err := DoSomething(ctx)
// if err != nil {
// return err
// }
// select {
// case <-ctx.Done():
// return ctx.Err()
// case out <- v:
// }
// }
// }
//
// See https://blog.golang.org/pipelines for more examples of how to use
// a Done channel for cancelation.
func (c *Context) Done() <-chan struct{} {
return c.Request.Context().Done()
}
// Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
func (c *Context) Err() error {
return c.Request.Context().Err()
}
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
//
// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
//
// Packages that define a Context key should provide type-safe accessors
// for the values stored using that key:
//
// // Package user defines a User type that's stored in Contexts.
// package user
//
// import "context"
//
// // User is the type of value stored in the Contexts.
// type User struct {...}
//
// // key is an unexported type for keys defined in this package.
// // This prevents collisions with keys defined in other packages.
// type key int
//
// // userKey is the key for user.User values in Contexts. It is
// // unexported; clients use user.NewContext and user.FromContext
// // instead of using this key directly.
// var userKey key = 0
//
// // NewContext returns a new Context that carries value u.
// func NewContext(ctx context.Context, u *User) context.Context {
// return context.WithValue(ctx, userKey, u)
// }
//
// // FromContext returns the User value stored in ctx, if any.
// func FromContext(ctx context.Context) (*User, bool) {
// u, ok := ctx.Value(userKey).(*User)
// return u, ok
// }
func (c *Context) Value(key interface{}) interface{} {
return c.Request.Context().Value(key)
}
// SetCancelContext This will set an external context.Context as a parent to this context so cancellations can be
// propagated quickly and reduce resource usage.
func (c *Context) SetCancelContext(ctx context.Context) *Context {
golRequestContext := context.WithValue(ctx, Key, c.Value(Key))
c.Request = c.Request.WithContext(golRequestContext)
return c
}
// emptyContext creates a new empty context.Context
func emptyContext() context.Context {
return context.WithValue(context.Background(), Key, Store{})
}
// createRequest creates a default http.Request instance.
func createRequest() *http.Request {
// Create HTTP request
req := &http.Request{
Method: "GET",
URL: &url.URL{},
Host: "",
ProtoMajor: 1,
ProtoMinor: 1,
Proto: "HTTP/1.1",
Header: make(http.Header),
Body: utils.NopCloser(),
}
// Return shallow copy of Request with the new context
return req.WithContext(emptyContext())
}
// createResponse creates a default http.Response instance.
func createResponse(req *http.Request) *http.Response {
return &http.Response{
ProtoMajor: 1,
ProtoMinor: 1,
Proto: "HTTP/1.1",
Request: req,
Header: make(http.Header),
Body: utils.NopCloser(),
}
}