33// license that can be found in the LICENSE file.
44
55// +build !appengine
6+ // +build go1.7
67
78package internal
89
6263 },
6364 }
6465
65- defaultTicketOnce sync.Once
66- defaultTicket string
66+ defaultTicketOnce sync.Once
67+ defaultTicket string
68+ backgroundContextOnce sync.Once
69+ backgroundContext netcontext.Context
6770)
6871
6972func apiURL () * url.URL {
@@ -87,16 +90,10 @@ func handleHTTP(w http.ResponseWriter, r *http.Request) {
8790 outHeader : w .Header (),
8891 apiURL : apiURL (),
8992 }
90- stopFlushing := make (chan int )
93+ r = r .WithContext (withContext (r .Context (), c ))
94+ c .req = r
9195
92- ctxs .Lock ()
93- ctxs .m [r ] = c
94- ctxs .Unlock ()
95- defer func () {
96- ctxs .Lock ()
97- delete (ctxs .m , r )
98- ctxs .Unlock ()
99- }()
96+ stopFlushing := make (chan int )
10097
10198 // Patch up RemoteAddr so it looks reasonable.
10299 if addr := r .Header .Get (userIPHeader ); addr != "" {
@@ -195,18 +192,6 @@ func renderPanic(x interface{}) string {
195192 return string (buf )
196193}
197194
198- var ctxs = struct {
199- sync.Mutex
200- m map [* http.Request ]* context
201- bg * context // background context, lazily initialized
202- // dec is used by tests to decorate the netcontext.Context returned
203- // for a given request. This allows tests to add overrides (such as
204- // WithAppIDOverride) to the context. The map is nil outside tests.
205- dec map [* http.Request ]func (netcontext.Context ) netcontext.Context
206- }{
207- m : make (map [* http.Request ]* context ),
208- }
209-
210195// context represents the context of an in-flight HTTP request.
211196// It implements the appengine.Context and http.ResponseWriter interfaces.
212197type context struct {
@@ -227,6 +212,32 @@ type context struct {
227212
228213var contextKey = "holds a *context"
229214
215+ // jointContext joins two contexts in a superficial way.
216+ // It takes values and timeouts from a base context, and only values from another context.
217+ type jointContext struct {
218+ base netcontext.Context
219+ valuesOnly netcontext.Context
220+ }
221+
222+ func (c jointContext ) Deadline () (time.Time , bool ) {
223+ return c .base .Deadline ()
224+ }
225+
226+ func (c jointContext ) Done () <- chan struct {} {
227+ return c .base .Done ()
228+ }
229+
230+ func (c jointContext ) Err () error {
231+ return c .base .Err ()
232+ }
233+
234+ func (c jointContext ) Value (key interface {}) interface {} {
235+ if val := c .base .Value (key ); val != nil {
236+ return val
237+ }
238+ return c .valuesOnly .Value (key )
239+ }
240+
230241// fromContext returns the App Engine context or nil if ctx is not
231242// derived from an App Engine context.
232243func fromContext (ctx netcontext.Context ) * context {
@@ -253,23 +264,15 @@ func IncomingHeaders(ctx netcontext.Context) http.Header {
253264 return nil
254265}
255266
256- func WithContext (parent netcontext.Context , req * http.Request ) netcontext.Context {
257- ctxs .Lock ()
258- c := ctxs .m [req ]
259- d := ctxs .dec [req ]
260- ctxs .Unlock ()
261-
262- if d != nil {
263- parent = d (parent )
264- }
267+ func ReqContext (req * http.Request ) netcontext.Context {
268+ return req .Context ()
269+ }
265270
266- if c == nil {
267- // Someone passed in an http.Request that is not in-flight.
268- // We panic here rather than panicking at a later point
269- // so that stack traces will be more sensible.
270- log .Panic ("appengine: NewContext passed an unknown http.Request" )
271+ func WithContext (parent netcontext.Context , req * http.Request ) netcontext.Context {
272+ return jointContext {
273+ base : parent ,
274+ valuesOnly : req .Context (),
271275 }
272- return withContext (parent , c )
273276}
274277
275278// DefaultTicket returns a ticket used for background context or dev_appserver.
@@ -291,60 +294,40 @@ func DefaultTicket() string {
291294}
292295
293296func BackgroundContext () netcontext.Context {
294- ctxs .Lock ()
295- defer ctxs .Unlock ()
296-
297- if ctxs .bg != nil {
298- return toContext (ctxs .bg )
299- }
300-
301- // Compute background security ticket.
302- ticket := DefaultTicket ()
303-
304- ctxs .bg = & context {
305- req : & http.Request {
306- Header : http.Header {
307- ticketHeader : []string {ticket },
297+ backgroundContextOnce .Do (func () {
298+ // Compute background security ticket.
299+ ticket := DefaultTicket ()
300+
301+ c := & context {
302+ req : & http.Request {
303+ Header : http.Header {
304+ ticketHeader : []string {ticket },
305+ },
308306 },
309- } ,
310- apiURL : apiURL (),
311- }
307+ apiURL : apiURL () ,
308+ }
309+ backgroundContext = toContext ( c )
312310
313- // TODO(dsymonds): Wire up the shutdown handler to do a final flush.
314- go ctxs .bg .logFlusher (make (chan int ))
311+ // TODO(dsymonds): Wire up the shutdown handler to do a final flush.
312+ go c .logFlusher (make (chan int ))
313+ })
315314
316- return toContext ( ctxs . bg )
315+ return backgroundContext
317316}
318317
319318// RegisterTestRequest registers the HTTP request req for testing, such that
320319// any API calls are sent to the provided URL. It returns a closure to delete
321320// the registration.
322321// It should only be used by aetest package.
323- func RegisterTestRequest (req * http.Request , apiURL * url.URL , decorate func (netcontext.Context ) netcontext.Context ) func () {
322+ func RegisterTestRequest (req * http.Request , apiURL * url.URL , decorate func (netcontext.Context ) netcontext.Context ) ( * http. Request , func () ) {
324323 c := & context {
325324 req : req ,
326325 apiURL : apiURL ,
327326 }
328- ctxs .Lock ()
329- defer ctxs .Unlock ()
330- if _ , ok := ctxs .m [req ]; ok {
331- log .Panic ("req already associated with context" )
332- }
333- if _ , ok := ctxs .dec [req ]; ok {
334- log .Panic ("req already associated with context" )
335- }
336- if ctxs .dec == nil {
337- ctxs .dec = make (map [* http.Request ]func (netcontext.Context ) netcontext.Context )
338- }
339- ctxs .m [req ] = c
340- ctxs .dec [req ] = decorate
341-
342- return func () {
343- ctxs .Lock ()
344- delete (ctxs .m , req )
345- delete (ctxs .dec , req )
346- ctxs .Unlock ()
347- }
327+ ctx := withContext (decorate (req .Context ()), c )
328+ req = req .WithContext (ctx )
329+ c .req = req
330+ return req , func () {}
348331}
349332
350333var errTimeout = & CallError {
0 commit comments