github.com/brycereitano/goa@v0.0.0-20170315073847-8ffa6c85e265/context.go (about)

     1  package goa
     2  
     3  import (
     4  	"net/http"
     5  	"net/url"
     6  	"strconv"
     7  
     8  	"golang.org/x/net/context"
     9  )
    10  
    11  // Keys used to store data in context.
    12  const (
    13  	reqKey key = iota + 1
    14  	respKey
    15  	ctrlKey
    16  	actionKey
    17  	paramsKey
    18  	logKey
    19  	logContextKey
    20  	errKey
    21  	securityScopesKey
    22  )
    23  
    24  type (
    25  	// RequestData provides access to the underlying HTTP request.
    26  	RequestData struct {
    27  		*http.Request
    28  
    29  		// Payload returns the decoded request body.
    30  		Payload interface{}
    31  		// Params contains the raw values for the parameters defined in the design including
    32  		// path parameters, query string parameters and header parameters.
    33  		Params url.Values
    34  	}
    35  
    36  	// ResponseData provides access to the underlying HTTP response.
    37  	ResponseData struct {
    38  		http.ResponseWriter
    39  
    40  		// The service used to encode the response.
    41  		Service *Service
    42  		// ErrorCode is the code of the error returned by the action if any.
    43  		ErrorCode string
    44  		// Status is the response HTTP status code.
    45  		Status int
    46  		// Length is the response body length.
    47  		Length int
    48  	}
    49  
    50  	// key is the type used to store internal values in the context.
    51  	// Context provides typed accessor methods to these values.
    52  	key int
    53  )
    54  
    55  // NewContext builds a new goa request context.
    56  // If ctx is nil then context.Background() is used.
    57  func NewContext(ctx context.Context, rw http.ResponseWriter, req *http.Request, params url.Values) context.Context {
    58  	if ctx == nil {
    59  		ctx = context.Background()
    60  	}
    61  	request := &RequestData{Request: req, Params: params}
    62  	response := &ResponseData{ResponseWriter: rw}
    63  	ctx = context.WithValue(ctx, respKey, response)
    64  	ctx = context.WithValue(ctx, reqKey, request)
    65  
    66  	return ctx
    67  }
    68  
    69  // WithAction creates a context with the given action name.
    70  func WithAction(ctx context.Context, action string) context.Context {
    71  	return context.WithValue(ctx, actionKey, action)
    72  }
    73  
    74  // WithLogger sets the request context logger and returns the resulting new context.
    75  func WithLogger(ctx context.Context, logger LogAdapter) context.Context {
    76  	return context.WithValue(ctx, logKey, logger)
    77  }
    78  
    79  // WithLogContext instantiates a new logger by appending the given key/value pairs to the context
    80  // logger and setting the resulting logger in the context.
    81  func WithLogContext(ctx context.Context, keyvals ...interface{}) context.Context {
    82  	logger := ContextLogger(ctx)
    83  	if logger == nil {
    84  		return ctx
    85  	}
    86  	nl := logger.New(keyvals...)
    87  	return WithLogger(ctx, nl)
    88  }
    89  
    90  // WithError creates a context with the given error.
    91  func WithError(ctx context.Context, err error) context.Context {
    92  	return context.WithValue(ctx, errKey, err)
    93  }
    94  
    95  // ContextController extracts the controller name from the given context.
    96  func ContextController(ctx context.Context) string {
    97  	if c := ctx.Value(ctrlKey); c != nil {
    98  		return c.(string)
    99  	}
   100  	return "<unknown>"
   101  }
   102  
   103  // ContextAction extracts the action name from the given context.
   104  func ContextAction(ctx context.Context) string {
   105  	if a := ctx.Value(actionKey); a != nil {
   106  		return a.(string)
   107  	}
   108  	return "<unknown>"
   109  }
   110  
   111  // ContextRequest extracts the request data from the given context.
   112  func ContextRequest(ctx context.Context) *RequestData {
   113  	if r := ctx.Value(reqKey); r != nil {
   114  		return r.(*RequestData)
   115  	}
   116  	return nil
   117  }
   118  
   119  // ContextResponse extracts the response data from the given context.
   120  func ContextResponse(ctx context.Context) *ResponseData {
   121  	if r := ctx.Value(respKey); r != nil {
   122  		return r.(*ResponseData)
   123  	}
   124  	return nil
   125  }
   126  
   127  // ContextLogger extracts the logger from the given context.
   128  func ContextLogger(ctx context.Context) LogAdapter {
   129  	if v := ctx.Value(logKey); v != nil {
   130  		return v.(LogAdapter)
   131  	}
   132  	return nil
   133  }
   134  
   135  // ContextError extracts the error from the given context.
   136  func ContextError(ctx context.Context) error {
   137  	if err := ctx.Value(errKey); err != nil {
   138  		return err.(error)
   139  	}
   140  	return nil
   141  }
   142  
   143  // SwitchWriter overrides the underlying response writer. It returns the response
   144  // writer that was previously set.
   145  func (r *ResponseData) SwitchWriter(rw http.ResponseWriter) http.ResponseWriter {
   146  	rwo := r.ResponseWriter
   147  	r.ResponseWriter = rw
   148  	return rwo
   149  }
   150  
   151  // Written returns true if the response was written, false otherwise.
   152  func (r *ResponseData) Written() bool {
   153  	return r.Status != 0
   154  }
   155  
   156  // WriteHeader records the response status code and calls the underlying writer.
   157  func (r *ResponseData) WriteHeader(status int) {
   158  	go IncrCounter([]string{"goa", "response", strconv.Itoa(status)}, 1.0)
   159  	r.Status = status
   160  	r.ResponseWriter.WriteHeader(status)
   161  }
   162  
   163  // Write records the amount of data written and calls the underlying writer.
   164  func (r *ResponseData) Write(b []byte) (int, error) {
   165  	r.Length += len(b)
   166  	return r.ResponseWriter.Write(b)
   167  }