@@ -6,221 +6,133 @@ import (
6
6
"net/url"
7
7
8
8
"golang.org/x/net/context"
9
- log "gopkg.in/inconshreveable/log15.v2"
10
9
)
11
10
12
- // Context is the object that provides access to the underlying HTTP request and response state.
13
- // Context implements http.ResponseWriter and also provides helper methods for writing HTTP responses.
14
- // It also implements the context.Context interface described at http://blog.golang.org/context.
15
- type Context struct {
16
- context.Context // Underlying context
17
- log.Logger // Context logger
18
- }
19
-
20
- // key is the type used to store internal values in the context.
21
- // Context provides typed accessor methods to these values.
22
- type key int
23
-
11
+ // Keys used to store data in context.
24
12
const (
25
- serviceKey = iota
26
- reqKey
13
+ reqKey key = iota + 1
27
14
respKey
28
- paramsKey
29
- payloadKey
30
- respWrittenKey
31
- respStatusKey
32
- respLenKey
33
15
)
34
16
35
- // NewContext builds a goa context from the given context.Context and request state.
36
- // If gctx is nil then context.Background is used instead.
37
- func NewContext (gctx context.Context ,
38
- service Service ,
39
- req * http.Request ,
40
- rw http.ResponseWriter ,
41
- params url.Values ) * Context {
42
-
43
- if gctx == nil {
44
- gctx = context .Background ()
17
+ type (
18
+ // RequestData provides access to the underlying HTTP request.
19
+ RequestData struct {
20
+ * http.Request
21
+
22
+ // Action is the name of the resource action targeted by the request
23
+ Action string
24
+ // Controller is the name of the resource controller targeted by the request
25
+ Controller string
26
+ // Service is the service targeted by the request
27
+ Service Service
28
+ // Payload returns the decoded request body.
29
+ Payload interface {}
30
+ // Params is the path and querystring request parameters.
31
+ Params url.Values
45
32
}
46
- gctx = context .WithValue (gctx , serviceKey , service )
47
- gctx = context .WithValue (gctx , reqKey , req )
48
- gctx = context .WithValue (gctx , respKey , rw )
49
- gctx = context .WithValue (gctx , paramsKey , params )
50
-
51
- return & Context {Context : gctx }
52
- }
53
-
54
- // SetPayload initializes the unmarshaled request body value.
55
- func (ctx * Context ) SetPayload (payload interface {}) {
56
- ctx .SetValue (payloadKey , payload )
57
- }
58
-
59
- // SetValue sets the value associated with key in the context.
60
- // The value can be retrieved using the Value method.
61
- // Note that this changes the underlying context.Context object and thus clients holding a reference
62
- // to that won't be able to access the new value. It's probably a bad idea to hold a reference to
63
- // the inner context anyway...
64
- func (ctx * Context ) SetValue (key , val interface {}) {
65
- ctx .Context = context .WithValue (ctx .Context , key , val )
66
- }
67
33
68
- // SetResponseWriter overrides the context underlying response writer. It returns the response
69
- // writer that was previously set.
70
- func (ctx * Context ) SetResponseWriter (rw http.ResponseWriter ) http.ResponseWriter {
71
- rwo := ctx .Value (respKey )
72
- ctx .SetValue (respKey , rw )
73
- if rwo == nil {
74
- return nil
75
- }
76
- return rwo .(http.ResponseWriter )
77
- }
34
+ // ResponseData provides access to the underlying HTTP response.
35
+ ResponseData struct {
36
+ http.ResponseWriter
37
+ ctx context.Context // for access to the encoder
78
38
79
- // Service returns the underlying service.
80
- func (ctx * Context ) Service () Service {
81
- s := ctx .Value (serviceKey )
82
- if s != nil {
83
- return s .(Service )
39
+ // Status is the response HTTP status code
40
+ Status int
41
+ // Len is the response body length
42
+ Len int
84
43
}
85
- return nil
86
- }
87
44
88
- // Request returns the underlying HTTP request.
89
- func (ctx * Context ) Request () * http.Request {
90
- r := ctx .Value (reqKey )
91
- if r != nil {
92
- return r .(* http.Request )
93
- }
94
- return nil
95
- }
45
+ // key is the type used to store internal values in the context.
46
+ // Context provides typed accessor methods to these values.
47
+ key int
48
+ )
96
49
97
- // ResponseWritten returns true if an HTTP response was written.
98
- func (ctx * Context ) ResponseWritten () bool {
99
- if wr := ctx .Value (respStatusKey ); wr != nil {
100
- return true
101
- }
102
- return false
103
- }
50
+ // NewContext builds a goa context from the given context.Context and request state.
51
+ // If gctx is nil then RootContext is used.
52
+ func NewContext (gctx context.Context , ctrl * ApplicationController , action string ,
53
+ req * http.Request , rw http.ResponseWriter , params url.Values ) context.Context {
104
54
105
- // ResponseStatus returns the response status if it was set via one of the context response
106
- // methods (Respond, JSON, BadRequest, Bug), 0 otherwise.
107
- func (ctx * Context ) ResponseStatus () int {
108
- if is := ctx .Value (respStatusKey ); is != nil {
109
- return is .(int )
55
+ if gctx == nil {
56
+ gctx = RootContext
110
57
}
111
- return 0
112
- }
113
-
114
- // ResponseLength returns the response body length in bytes if the response was written to the
115
- // context via one of the response methods (Respond, JSON, BadRequest, Bug), 0 otherwise.
116
- func (ctx * Context ) ResponseLength () int {
117
- if is := ctx .Value (respLenKey ); is != nil {
118
- return is .(int )
58
+ request := & RequestData {
59
+ Request : req ,
60
+ Params : params ,
61
+ Action : action ,
62
+ Controller : ctrl .Name ,
63
+ Service : ctrl .app ,
119
64
}
120
- return 0
121
- }
65
+ response := & ResponseData {ResponseWriter : rw }
66
+ gctx = context .WithValue (gctx , reqKey , request )
67
+ gctx = context .WithValue (gctx , respKey , response )
68
+ response .ctx = gctx
122
69
123
- // Get returns the param or querystring value with the given name.
124
- func (ctx * Context ) Get (name string ) string {
125
- iparams := ctx .Value (paramsKey )
126
- if iparams == nil {
127
- return ""
128
- }
129
- params := iparams .(url.Values )
130
- return params .Get (name )
70
+ return gctx
131
71
}
132
72
133
- // GetMany returns the querystring values with the given name or nil if there aren't any .
134
- func (ctx * Context ) GetMany ( name string ) [] string {
135
- iparams := ctx .Value (paramsKey )
136
- if iparams = = nil {
137
- return nil
73
+ // Request gives access to the underlying HTTP request .
74
+ func Request (ctx context. Context ) * RequestData {
75
+ r := ctx .Value (reqKey )
76
+ if r ! = nil {
77
+ return r .( * RequestData )
138
78
}
139
- params := iparams .(url.Values )
140
- return params [name ]
79
+ return nil
141
80
}
142
81
143
- // GetNames returns all the querystring and URL parameter names.
144
- func (ctx * Context ) GetNames () []string {
145
- iparams := ctx .Value (paramsKey )
146
- if iparams == nil {
147
- return nil
148
- }
149
- params := iparams .(url.Values )
150
- names := make ([]string , len (params ))
151
- i := 0
152
- for n := range params {
153
- names [i ] = n
154
- i ++
82
+ // Response gives access to the underlying HTTP response.
83
+ func Response (ctx context.Context ) * ResponseData {
84
+ r := ctx .Value (respKey )
85
+ if r != nil {
86
+ return r .(* ResponseData )
155
87
}
156
- return names
88
+ return nil
157
89
}
158
90
159
- // AllParams return all URL and querystring parameters.
160
- func (ctx * Context ) AllParams () url.Values {
161
- iparams := ctx .Value (paramsKey )
162
- return iparams .(url.Values )
91
+ // SetPayload initializes the unmarshaled request body value.
92
+ func (r * RequestData ) SetPayload (payload interface {}) {
93
+ r .Payload = payload
163
94
}
164
95
165
- // RawPayload returns the deserialized request body or nil if body is empty.
166
- func (ctx * Context ) RawPayload () interface {} {
167
- return ctx .Value (payloadKey )
96
+ // SwitchResponseWriter overrides the underlying response writer. It returns the response
97
+ // writer that was previously set.
98
+ func (r * ResponseData ) SwitchResponseWriter (rw http.ResponseWriter ) http.ResponseWriter {
99
+ rwo := r .ResponseWriter
100
+ r .ResponseWriter = rw
101
+ return rwo
168
102
}
169
103
170
- // RespondBytes writes the given HTTP status code and response body.
171
- // This method should only be called once per request.
172
- func (ctx * Context ) RespondBytes (code int , body []byte ) error {
173
- ctx .WriteHeader (code )
174
- if _ , err := ctx .Write (body ); err != nil {
175
- return err
176
- }
177
- return nil
104
+ // Written returns true if the response was written.
105
+ func (r * ResponseData ) Written () bool {
106
+ return r .Status != 0
178
107
}
179
108
180
- // Respond serializes the given body matching the request Accept header against the service
109
+ // Send serializes the given body matching the request Accept header against the service
181
110
// encoders. It uses the default service encoder if no match is found.
182
- func (ctx * Context ) Respond (code int , body interface {}) error {
183
- ctx .WriteHeader (code )
184
- return ctx .Service () .EncodeResponse (ctx , body )
111
+ func (r * ResponseData ) Send (code int , body interface {}) error {
112
+ r .WriteHeader (code )
113
+ return Request ( r . ctx ) .Service .EncodeResponse (r . ctx , body )
185
114
}
186
115
187
116
// BadRequest sends a HTTP response with status code 400 and the given error as body.
188
- func (ctx * Context ) BadRequest (err * BadRequestError ) error {
189
- return ctx . Respond (400 , err .Error ())
117
+ func (r * ResponseData ) BadRequest (err * BadRequestError ) error {
118
+ return r . Send (400 , err .Error ())
190
119
}
191
120
192
121
// Bug sends a HTTP response with status code 500 and the given body.
193
122
// The body can be set using a format and substituted values a la fmt.Printf.
194
- func (ctx * Context ) Bug (format string , a ... interface {}) error {
123
+ func (r * ResponseData ) Bug (format string , a ... interface {}) error {
195
124
body := fmt .Sprintf (format , a ... )
196
- return ctx . Respond (500 , body )
125
+ return r . Send (500 , body )
197
126
}
198
127
199
- // Header returns the response header. It implements the http.ResponseWriter interface.
200
- func (ctx * Context ) Header () http.Header {
201
- rw := ctx .Value (respKey )
202
- if rw != nil {
203
- return rw .(http.ResponseWriter ).Header ()
204
- }
205
- return nil
206
- }
207
-
208
- // WriteHeader writes the HTTP status code to the response. It implements the
209
- // http.ResponseWriter interface.
210
- func (ctx * Context ) WriteHeader (code int ) {
211
- rw := ctx .Value (respKey )
212
- if rw != nil {
213
- ctx .Context = context .WithValue (ctx .Context , respStatusKey , code )
214
- rw .(http.ResponseWriter ).WriteHeader (code )
215
- }
128
+ // WriteHeader records the response status code and calls the underlying writer.
129
+ func (r * ResponseData ) WriteHeader (status int ) {
130
+ r .Status = status
131
+ r .ResponseWriter .WriteHeader (status )
216
132
}
217
133
218
- // Write writes the HTTP response body. It implements the http.ResponseWriter interface.
219
- func (ctx * Context ) Write (body []byte ) (int , error ) {
220
- rw := ctx .Value (respKey )
221
- if rw != nil {
222
- ctx .Context = context .WithValue (ctx .Context , respLenKey , ctx .ResponseLength ()+ len (body ))
223
- return rw .(http.ResponseWriter ).Write (body )
224
- }
225
- return 0 , fmt .Errorf ("response writer not initialized" )
134
+ // Write records the amount of data written and calls the underlying writer.
135
+ func (r * ResponseData ) Write (b []byte ) (int , error ) {
136
+ r .Len += len (b )
137
+ return r .ResponseWriter .Write (b )
226
138
}
0 commit comments