Merge lp:~chipaca/ubuntu-push/the-merge-automatic into lp:ubuntu-push
- the-merge-automatic
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | John Lenton |
Approved revision: | 109 |
Merged at revision: | 105 |
Proposed branch: | lp:~chipaca/ubuntu-push/the-merge-automatic |
Merge into: | lp:ubuntu-push |
Diff against target: |
8895 lines (+4002/-1767) 75 files modified
Makefile (+2/-2) PACKAGE_DEPS (+2/-0) bus/connectivity/connectivity_test.go (+7/-7) bus/endpoint.go (+14/-10) bus/notifications/app_helper/app_helper_c.go (+41/-0) bus/notifications/raw.go (+36/-0) bus/notifications/raw_test.go (+43/-0) bus/testing/testing_endpoint.go (+3/-3) bus/testing/testing_endpoint_test.go (+6/-6) client/client.go (+90/-90) client/client_test.go (+113/-58) client/gethosts/gethost_test.go (+1/-1) client/service/common.go (+99/-0) client/service/postal.go (+181/-0) client/service/postal_test.go (+252/-0) client/service/service.go (+155/-167) client/service/service_test.go (+166/-136) client/session/session.go (+6/-17) client/session/session_test.go (+15/-28) debian/changelog (+34/-0) debian/config.json (+3/-1) debian/control (+2/-0) debian/rules (+6/-1) external/README (+1/-1) external/murmur3/murmur128.go (+2/-2) external/murmur3/murmur32.go (+2/-2) external/murmur3/murmur64.go (+1/-1) external/murmur3/murmur_test.go (+9/-5) http13client/Makefile (+2/-1) http13client/_patches/empty_server.patch (+118/-42) http13client/_patches/fix_code.patch (+73/-65) http13client/_patches/fix_status.patch (+33/-20) http13client/_patches/fix_tests.patch (+338/-384) http13client/_using.txt (+3/-3) http13client/client.go (+11/-3) http13client/client_test.go (+92/-0) http13client/cookie.go (+19/-37) http13client/cookie_test.go (+0/-304) http13client/request.go (+62/-50) http13client/request_test.go (+48/-8) http13client/response.go (+52/-5) http13client/response_test.go (+4/-0) http13client/responsewrite_test.go (+117/-1) http13client/server.go (+11/-5) http13client/transfer.go (+59/-27) http13client/transport.go (+114/-42) http13client/transport_test.go (+296/-17) launch_helper/helper.go (+51/-0) launch_helper/helper_output.go (+57/-0) launch_helper/helper_test.go (+57/-0) messaging/cmessaging/cmessaging.go (+71/-0) messaging/cmessaging/cmessaging_c.go (+56/-0) messaging/messaging.go (+50/-0) messaging/messaging_test.go (+87/-0) messaging/reply/reply.go (+25/-0) nih/nih.go (+1/-1) scripts/deps.sh (+1/-1) scripts/dummyauth.sh (+3/-0) scripts/register (+43/-0) scripts/unicast (+2/-0) server/acceptance/acceptanceclient.go (+1/-1) server/acceptance/cmd/acceptanceclient.go (+3/-1) server/acceptance/suites/broadcast.go (+13/-13) server/acceptance/suites/suite.go (+4/-2) server/acceptance/suites/unicast.go (+16/-8) server/api/handlers.go (+179/-93) server/api/handlers_test.go (+234/-42) server/session/session.go (+79/-29) server/session/session_test.go (+95/-4) server/store/inmemory.go (+31/-0) server/store/inmemory_test.go (+51/-0) server/store/store.go (+10/-0) signing-helper/signing-helper.cpp (+6/-10) testing/helpers.go (+27/-0) whoopsie/identifier/identifier.go (+5/-10) |
To merge this branch: | bzr merge lp:~chipaca/ubuntu-push/the-merge-automatic |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Push Hackers | Pending | ||
Review via email: mp+224289@code.launchpad.net |
Commit message
Description of the change
from debian/changelog:
[ Samuele Pedroni ]
* Support registering tokens and sending notifications with a token
* Register script and scripts unicast support
* Update http13client from the actual go1.3 release
* Avoid late pings in the face of nop exchanges
* murmur3 upstream change of seed to 0
[ Roberto Alsina ]
* Make signing-helper generate a HTTP header instead of a querystring,
and take a URL to sign.
* Wrap libmessaging-menu to allow for persistent notifications.
* Wrap ubuntu-app-launch start_helper / stop_helper functions.
[ John R. Lenton ]
* Switch dbus api to retrieve app name from dbus path.
* Move signing bits up from session to client, for reuse by service.
* Change AuthHelper to be a string; auth helper should now expect a
parameter (the url to sign). Added SessionURL to config.
* Adapt our whoopsie wrapper to whoopsie's now more correct behavior wrt
failing to get a mac address.
* Add registration_url to config; hook up auth bits and reg url to
client & service.
* Do an HTTP POST to registration_url on register.
* Fix debian/rules so packaging-time tests pass (ugh)
[ Guillermo Gonzalez ]
* Split DBus service into PushService and PostalService
Preview Diff
1 | === modified file 'Makefile' |
2 | --- Makefile 2014-06-02 11:37:03 +0000 |
3 | +++ Makefile 2014-07-02 13:12:36 +0000 |
4 | @@ -36,8 +36,8 @@ |
5 | $(GOPATH)/bin/godeps -u dependencies.tsv |
6 | go install $(GODEPS) |
7 | |
8 | -dependencies.tsv: $(TOBUILD) |
9 | - $(GOPATH)/bin/godeps -t $(foreach i,$^,$(dir $(PROJECT)/$(i))) 2>/dev/null | cat > $@ |
10 | +dependencies.tsv: |
11 | + $(GOPATH)/bin/godeps -t $(TOTEST) $(foreach i,$(TOBUILD),$(dir $(PROJECT)/$(i))) 2>/dev/null | cat > $@ |
12 | |
13 | check: |
14 | go test $(TESTFLAGS) $(TOTEST) |
15 | |
16 | === modified file 'PACKAGE_DEPS' |
17 | --- PACKAGE_DEPS 2014-05-23 06:21:46 +0000 |
18 | +++ PACKAGE_DEPS 2014-07-02 13:12:36 +0000 |
19 | @@ -8,3 +8,5 @@ |
20 | libsqlite3-dev |
21 | libubuntuoneauth-2.0-dev |
22 | libwhoopsie-dev |
23 | +libmessaging-menu-dev |
24 | +libubuntu-app-launch2-dev |
25 | |
26 | === modified file 'bus/connectivity/connectivity_test.go' |
27 | --- bus/connectivity/connectivity_test.go 2014-05-09 10:48:39 +0000 |
28 | +++ bus/connectivity/connectivity_test.go 2014-07-02 13:12:36 +0000 |
29 | @@ -160,13 +160,13 @@ |
30 | return nil |
31 | } |
32 | |
33 | -func (*racyEndpoint) Close() {} |
34 | -func (*racyEndpoint) Dial() error { return nil } |
35 | -func (*racyEndpoint) String() string { return "racyEndpoint" } |
36 | -func (*racyEndpoint) Call(string, []interface{}, ...interface{}) error { return nil } |
37 | -func (*racyEndpoint) GrabName(bool) <-chan error { return nil } |
38 | -func (*racyEndpoint) WatchMethod(bus.DispatchMap, ...interface{}) {} |
39 | -func (*racyEndpoint) Signal(member string, args []interface{}) error { return nil } |
40 | +func (*racyEndpoint) Close() {} |
41 | +func (*racyEndpoint) Dial() error { return nil } |
42 | +func (*racyEndpoint) String() string { return "racyEndpoint" } |
43 | +func (*racyEndpoint) Call(string, []interface{}, ...interface{}) error { return nil } |
44 | +func (*racyEndpoint) GrabName(bool) <-chan error { return nil } |
45 | +func (*racyEndpoint) WatchMethod(bus.DispatchMap, string, ...interface{}) {} |
46 | +func (*racyEndpoint) Signal(member string, suffix string, args []interface{}) error { return nil } |
47 | |
48 | var _ bus.Endpoint = (*racyEndpoint)(nil) |
49 | |
50 | |
51 | === modified file 'bus/endpoint.go' |
52 | --- bus/endpoint.go 2014-05-15 11:28:06 +0000 |
53 | +++ bus/endpoint.go 2014-07-02 13:12:36 +0000 |
54 | @@ -21,7 +21,9 @@ |
55 | import ( |
56 | "errors" |
57 | "fmt" |
58 | + |
59 | "launchpad.net/go-dbus/v1" |
60 | + |
61 | "launchpad.net/ubuntu-push/logger" |
62 | ) |
63 | |
64 | @@ -29,15 +31,15 @@ |
65 | * Endpoint (and its implementation) |
66 | */ |
67 | |
68 | -type BusMethod func([]interface{}, []interface{}) ([]interface{}, error) |
69 | +type BusMethod func(string, []interface{}, []interface{}) ([]interface{}, error) |
70 | type DispatchMap map[string]BusMethod |
71 | |
72 | // bus.Endpoint represents the DBus connection itself. |
73 | type Endpoint interface { |
74 | GrabName(allowReplacement bool) <-chan error |
75 | WatchSignal(member string, f func(...interface{}), d func()) error |
76 | - WatchMethod(DispatchMap, ...interface{}) |
77 | - Signal(string, []interface{}) error |
78 | + WatchMethod(DispatchMap, string, ...interface{}) |
79 | + Signal(string, string, []interface{}) error |
80 | Call(member string, args []interface{}, rvs ...interface{}) error |
81 | GetProperty(property string) (interface{}, error) |
82 | Dial() error |
83 | @@ -212,8 +214,9 @@ |
84 | // Signal() sends out a signal called <member> containing <args>. |
85 | // |
86 | // XXX: untested |
87 | -func (endp *endpoint) Signal(member string, args []interface{}) error { |
88 | - msg := dbus.NewSignalMessage(dbus.ObjectPath(endp.addr.Path), endp.addr.Interface, member) |
89 | +func (endp *endpoint) Signal(member string, suffix string, args []interface{}) error { |
90 | + path := dbus.ObjectPath(endp.addr.Path + suffix) |
91 | + msg := dbus.NewSignalMessage(path, endp.addr.Interface, member) |
92 | if args != nil { |
93 | err := msg.AppendArgs(args...) |
94 | if err != nil { |
95 | @@ -234,7 +237,7 @@ |
96 | // calls. |
97 | // |
98 | // XXX: untested |
99 | -func (endp *endpoint) WatchMethod(dispatch DispatchMap, extra ...interface{}) { |
100 | +func (endp *endpoint) WatchMethod(dispatch DispatchMap, suffix string, extra ...interface{}) { |
101 | ch := make(chan *dbus.Message) |
102 | go func() { |
103 | var reply *dbus.Message |
104 | @@ -249,12 +252,12 @@ |
105 | endp.log.Errorf("WatchMethod: unknown method %s", msg.Member) |
106 | } else { |
107 | args := msg.AllArgs() |
108 | - rvals, err := meth(args, extra) |
109 | + rvals, err := meth(string(msg.Path), args, extra) |
110 | if err != nil { |
111 | reply = dbus.NewErrorMessage(msg, err_iface, err.Error()) |
112 | - endp.log.Errorf("WatchMethod: %s(%#v, %#v) failure: %#v", msg.Member, args, extra, err) |
113 | + endp.log.Errorf("WatchMethod: %s(%v, %#v, %#v) failure: %#v", msg.Member, msg.Path, args, extra, err) |
114 | } else { |
115 | - endp.log.Debugf("WatchMethod: %s(%#v, %#v) success: %#v", msg.Member, args, extra, rvals) |
116 | + endp.log.Debugf("WatchMethod: %s(%v, %#v, %#v) success: %#v", msg.Member, msg.Path, args, extra, rvals) |
117 | reply = dbus.NewMethodReturnMessage(msg) |
118 | err = reply.AppendArgs(rvals...) |
119 | if err != nil { |
120 | @@ -270,7 +273,8 @@ |
121 | |
122 | } |
123 | }() |
124 | - endp.bus.RegisterObjectPath(dbus.ObjectPath(endp.addr.Path), ch) |
125 | + path := dbus.ObjectPath(endp.addr.Path + suffix) |
126 | + endp.bus.RegisterObjectPath(path, ch) |
127 | } |
128 | |
129 | /* |
130 | |
131 | === added directory 'bus/notifications/app_helper' |
132 | === added file 'bus/notifications/app_helper/app_helper_c.go' |
133 | --- bus/notifications/app_helper/app_helper_c.go 1970-01-01 00:00:00 +0000 |
134 | +++ bus/notifications/app_helper/app_helper_c.go 2014-07-02 13:12:36 +0000 |
135 | @@ -0,0 +1,41 @@ |
136 | +/* |
137 | + Copyright 2013-2014 Canonical Ltd. |
138 | + |
139 | + This program is free software: you can redistribute it and/or modify it |
140 | + under the terms of the GNU General Public License version 3, as published |
141 | + by the Free Software Foundation. |
142 | + |
143 | + This program is distributed in the hope that it will be useful, but |
144 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
145 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
146 | + PURPOSE. See the GNU General Public License for more details. |
147 | + |
148 | + You should have received a copy of the GNU General Public License along |
149 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
150 | +*/ |
151 | + |
152 | +// Package app_helper wraps C functions to access app information |
153 | +package app_helper |
154 | + |
155 | +/* |
156 | +#cgo pkg-config: gio-unix-2.0 |
157 | +#cgo pkg-config: gio-2.0 |
158 | +#include <stdlib.h> |
159 | +#include <glib.h> |
160 | +#include <gio/gdesktopappinfo.h> |
161 | +*/ |
162 | +import "C" |
163 | +import "unsafe" |
164 | + |
165 | +func AppIconFromId(appId string) string { |
166 | + _id := C.CString(appId) |
167 | + defer C.free(unsafe.Pointer(_id)) |
168 | + _app_info := C.g_desktop_app_info_new(_id) |
169 | + defer C.g_app_info_delete(_app_info) |
170 | + _app_icon := C.g_app_info_get_icon(_app_info) |
171 | + defer C.g_object_unref((C.gpointer)(_app_icon)) |
172 | + _icon_string := C.g_icon_to_string(_app_icon) |
173 | + defer C.free(unsafe.Pointer(_icon_string)) |
174 | + name := C.GoString((*C.char)(_icon_string)) |
175 | + return name |
176 | +} |
177 | |
178 | === modified file 'bus/notifications/raw.go' |
179 | --- bus/notifications/raw.go 2014-05-20 10:50:04 +0000 |
180 | +++ bus/notifications/raw.go 2014-07-02 13:12:36 +0000 |
181 | @@ -23,9 +23,12 @@ |
182 | |
183 | import ( |
184 | "errors" |
185 | + "fmt" |
186 | |
187 | "launchpad.net/go-dbus/v1" |
188 | "launchpad.net/ubuntu-push/bus" |
189 | + c_helper "launchpad.net/ubuntu-push/bus/notifications/app_helper" |
190 | + "launchpad.net/ubuntu-push/launch_helper" |
191 | "launchpad.net/ubuntu-push/logger" |
192 | ) |
193 | |
194 | @@ -96,3 +99,36 @@ |
195 | } |
196 | return ch, nil |
197 | } |
198 | + |
199 | +// ShowCard displays a given card. |
200 | +// |
201 | +// If card.Actions has 1 action, it's an interactive notification. |
202 | +// If card.Actions has 2 or more actions, it will show as a snap decision. |
203 | +// |
204 | +// WatchActions will receive something like this in the ActionId field: |
205 | +// appId::notificationId::action.Id |
206 | +func (raw *RawNotifications) Present(appId string, notificationId string, notification *launch_helper.Notification) (uint32, error) { |
207 | + if notification == nil || notification.Card == nil || !notification.Card.Popup || notification.Card.Summary == "" { |
208 | + raw.log.Debugf("skipping notification: nil, or nil card, or not popup, or no summary: %#v", notification) |
209 | + return 0, nil |
210 | + } |
211 | + |
212 | + card := notification.Card |
213 | + |
214 | + app_icon := c_helper.AppIconFromId(appId) |
215 | + hints := make(map[string]*dbus.Variant) |
216 | + hints["x-canonical-secondary-icon"] = &dbus.Variant{app_icon} |
217 | + |
218 | + actions := make([]string, 2*len(card.Actions)) |
219 | + for i, action := range card.Actions { |
220 | + actions[2*i] = fmt.Sprintf("%s::%s::%d", appId, notificationId, i) |
221 | + actions[2*i+1] = action |
222 | + } |
223 | + switch len(actions) { |
224 | + case 2: |
225 | + hints["x-canonical-switch-to-application"] = &dbus.Variant{true} |
226 | + case 4: |
227 | + hints["x-canonical-snap-decisions"] = &dbus.Variant{true} |
228 | + } |
229 | + return raw.Notify(appId, 0, card.Icon, card.Summary, card.Body, actions, hints, 30*1000) |
230 | +} |
231 | |
232 | === modified file 'bus/notifications/raw_test.go' |
233 | --- bus/notifications/raw_test.go 2014-05-20 10:50:04 +0000 |
234 | +++ bus/notifications/raw_test.go 2014-07-02 13:12:36 +0000 |
235 | @@ -22,6 +22,7 @@ |
236 | import ( |
237 | . "launchpad.net/gocheck" |
238 | testibus "launchpad.net/ubuntu-push/bus/testing" |
239 | + "launchpad.net/ubuntu-push/launch_helper" |
240 | "launchpad.net/ubuntu-push/logger" |
241 | helpers "launchpad.net/ubuntu-push/testing" |
242 | "launchpad.net/ubuntu-push/testing/condition" |
243 | @@ -95,3 +96,45 @@ |
244 | _, err := raw.WatchActions() |
245 | c.Check(err, NotNil) |
246 | } |
247 | + |
248 | +func (s *RawSuite) TestPresentNotifies(c *C) { |
249 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1)) |
250 | + raw := Raw(endp, s.log) |
251 | + nid, err := raw.Present("firefox.desktop", "notifId", &launch_helper.Notification{Card: &launch_helper.Card{Summary: "summary", Popup: true}}) |
252 | + c.Check(err, IsNil) |
253 | + c.Check(nid, Equals, uint32(1)) |
254 | +} |
255 | + |
256 | +func (s *RawSuite) TestPresentNoNotificationDoesNotNotify(c *C) { |
257 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1)) |
258 | + raw := Raw(endp, s.log) |
259 | + nid, err := raw.Present("firefox.desktop", "notifId", nil) |
260 | + c.Check(err, IsNil) |
261 | + c.Check(nid, Equals, uint32(0)) |
262 | +} |
263 | + |
264 | +func (s *RawSuite) TestPresentNoCardDoesNotNotify(c *C) { |
265 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1)) |
266 | + raw := Raw(endp, s.log) |
267 | + nid, err := raw.Present("firefox.desktop", "notifId", &launch_helper.Notification{}) |
268 | + c.Check(err, IsNil) |
269 | + c.Check(nid, Equals, uint32(0)) |
270 | +} |
271 | + |
272 | +func (s *RawSuite) TestPresentNoSummaryDoesNotNotify(c *C) { |
273 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1)) |
274 | + raw := Raw(endp, s.log) |
275 | + nid, err := raw.Present("firefox.desktop", "notifId", &launch_helper.Notification{Card: &launch_helper.Card{}}) |
276 | + c.Check(err, IsNil) |
277 | + c.Check(nid, Equals, uint32(0)) |
278 | +} |
279 | + |
280 | +func (s *RawSuite) TestPresentNoPopupNoNotify(c *C) { |
281 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1)) |
282 | + raw := Raw(endp, s.log) |
283 | + nid, err := raw.Present("firefox.desktop", "notifId", &launch_helper.Notification{Card: &launch_helper.Card{Summary: "summary"}}) |
284 | + c.Check(err, IsNil) |
285 | + c.Check(nid, Equals, uint32(0)) |
286 | +} |
287 | + |
288 | +// XXX Missing test about ShowCard manipulating Actions and hints correctly. |
289 | |
290 | === modified file 'bus/testing/testing_endpoint.go' |
291 | --- bus/testing/testing_endpoint.go 2014-05-15 12:27:32 +0000 |
292 | +++ bus/testing/testing_endpoint.go 2014-07-02 13:12:36 +0000 |
293 | @@ -187,7 +187,7 @@ |
294 | return nil |
295 | } |
296 | |
297 | -func (tc *testingEndpoint) WatchMethod(dispatch bus.DispatchMap, extra ...interface{}) { |
298 | +func (tc *testingEndpoint) WatchMethod(dispatch bus.DispatchMap, suffix string, extra ...interface{}) { |
299 | tc.callArgsLck.Lock() |
300 | defer tc.callArgsLck.Unlock() |
301 | |
302 | @@ -196,12 +196,12 @@ |
303 | tc.callArgs = append(tc.callArgs, args) |
304 | } |
305 | |
306 | -func (tc *testingEndpoint) Signal(member string, args []interface{}) error { |
307 | +func (tc *testingEndpoint) Signal(member string, suffix string, args []interface{}) error { |
308 | tc.callArgsLck.Lock() |
309 | defer tc.callArgsLck.Unlock() |
310 | |
311 | callargs := callArgs{Member: "::Signal"} |
312 | - callargs.Args = append(callargs.Args, member, args) |
313 | + callargs.Args = append(callargs.Args, member, suffix, args) |
314 | tc.callArgs = append(tc.callArgs, callargs) |
315 | |
316 | return nil |
317 | |
318 | === modified file 'bus/testing/testing_endpoint_test.go' |
319 | --- bus/testing/testing_endpoint_test.go 2014-06-02 10:04:34 +0000 |
320 | +++ bus/testing/testing_endpoint_test.go 2014-07-02 13:12:36 +0000 |
321 | @@ -214,24 +214,24 @@ |
322 | // Test that Signal updates callArgs |
323 | func (s *TestingEndpointSuite) TestSignalUpdatesCallArgs(c *C) { |
324 | endp := NewTestingEndpoint(nil, condition.Work(true)) |
325 | - endp.Signal("hello", []interface{}{"world"}) |
326 | - endp.Signal("hello", []interface{}{"there"}) |
327 | + endp.Signal("hello", "", []interface{}{"world"}) |
328 | + endp.Signal("hello", "/potato", []interface{}{"there"}) |
329 | c.Check(GetCallArgs(endp), DeepEquals, []callArgs{ |
330 | { |
331 | Member: "::Signal", |
332 | - Args: []interface{}{"hello", []interface{}{"world"}}, |
333 | + Args: []interface{}{"hello", "", []interface{}{"world"}}, |
334 | }, { |
335 | Member: "::Signal", |
336 | - Args: []interface{}{"hello", []interface{}{"there"}}, |
337 | + Args: []interface{}{"hello", "/potato", []interface{}{"there"}}, |
338 | }}) |
339 | } |
340 | |
341 | // Test that WatchMethod updates callArgs |
342 | func (s *TestingEndpointSuite) TestWatchMethodUpdatesCallArgs(c *C) { |
343 | endp := NewTestingEndpoint(nil, condition.Work(true)) |
344 | - foo := func([]interface{}, []interface{}) ([]interface{}, error) { return nil, nil } |
345 | + foo := func(string, []interface{}, []interface{}) ([]interface{}, error) { return nil, nil } |
346 | foomp := bus.DispatchMap{"foo": foo} |
347 | - endp.WatchMethod(foomp) |
348 | + endp.WatchMethod(foomp, "/*") |
349 | c.Check(GetCallArgs(endp), DeepEquals, []callArgs{ |
350 | { |
351 | Member: "::WatchMethod", |
352 | |
353 | === modified file 'client/client.go' |
354 | --- client/client.go 2014-05-21 10:03:18 +0000 |
355 | +++ client/client.go 2014-07-02 13:12:36 +0000 |
356 | @@ -22,16 +22,14 @@ |
357 | "crypto/sha256" |
358 | "encoding/base64" |
359 | "encoding/hex" |
360 | - "encoding/json" |
361 | "encoding/pem" |
362 | "errors" |
363 | "fmt" |
364 | "io/ioutil" |
365 | "os" |
366 | + "os/exec" |
367 | "strings" |
368 | |
369 | - "launchpad.net/go-dbus/v1" |
370 | - |
371 | "launchpad.net/ubuntu-push/bus" |
372 | "launchpad.net/ubuntu-push/bus/connectivity" |
373 | "launchpad.net/ubuntu-push/bus/networkmanager" |
374 | @@ -63,40 +61,38 @@ |
375 | // The PEM-encoded server certificate |
376 | CertPEMFile string `json:"cert_pem_file"` |
377 | // How to invoke the auth helper |
378 | - AuthHelper []string `json:"auth_helper"` |
379 | + AuthHelper string `json:"auth_helper"` |
380 | + SessionURL string `json:"session_url"` |
381 | + RegistrationURL string `json:"registration_url"` |
382 | // The logging level (one of "debug", "info", "error") |
383 | LogLevel logger.ConfigLogLevel `json:"log_level"` |
384 | } |
385 | |
386 | // PushClient is the Ubuntu Push Notifications client-side daemon. |
387 | type PushClient struct { |
388 | - leveldbPath string |
389 | - configPath string |
390 | - config ClientConfig |
391 | - log logger.Logger |
392 | - pem []byte |
393 | - idder identifier.Id |
394 | - deviceId string |
395 | - notificationsEndp bus.Endpoint |
396 | - urlDispatcherEndp bus.Endpoint |
397 | - connectivityEndp bus.Endpoint |
398 | - systemImageEndp bus.Endpoint |
399 | - systemImageInfo *systemimage.InfoResult |
400 | - connCh chan bool |
401 | - hasConnectivity bool |
402 | - actionsCh <-chan notifications.RawActionReply |
403 | - session *session.ClientSession |
404 | - sessionConnectedCh chan uint32 |
405 | - serviceEndpoint bus.Endpoint |
406 | - service *service.Service |
407 | + leveldbPath string |
408 | + configPath string |
409 | + config ClientConfig |
410 | + log logger.Logger |
411 | + pem []byte |
412 | + idder identifier.Id |
413 | + deviceId string |
414 | + notificationsEndp bus.Endpoint |
415 | + urlDispatcherEndp bus.Endpoint |
416 | + connectivityEndp bus.Endpoint |
417 | + systemImageEndp bus.Endpoint |
418 | + systemImageInfo *systemimage.InfoResult |
419 | + connCh chan bool |
420 | + hasConnectivity bool |
421 | + actionsCh <-chan notifications.RawActionReply |
422 | + session *session.ClientSession |
423 | + sessionConnectedCh chan uint32 |
424 | + pushServiceEndpoint bus.Endpoint |
425 | + pushService *service.PushService |
426 | + postalServiceEndpoint bus.Endpoint |
427 | + postalService *service.PostalService |
428 | } |
429 | |
430 | -var ( |
431 | - system_update_url = "settings:///system/system-update" |
432 | - ACTION_ID_SNOWFLAKE = "::ubuntu-push-client::" |
433 | - ACTION_ID_BROADCAST = ACTION_ID_SNOWFLAKE + system_update_url |
434 | -) |
435 | - |
436 | // Creates a new Ubuntu Push Notifications client-side daemon that will use |
437 | // the given configuration file. |
438 | func NewPushClient(configPath string, leveldbPath string) *PushClient { |
439 | @@ -128,10 +124,12 @@ |
440 | |
441 | // overridden for testing |
442 | client.idder = identifier.New() |
443 | - client.notificationsEndp = bus.SessionBus.Endpoint(notifications.BusAddress, client.log) |
444 | client.urlDispatcherEndp = bus.SessionBus.Endpoint(urldispatcher.BusAddress, client.log) |
445 | client.connectivityEndp = bus.SystemBus.Endpoint(networkmanager.BusAddress, client.log) |
446 | client.systemImageEndp = bus.SystemBus.Endpoint(systemimage.BusAddress, client.log) |
447 | + if client.notificationsEndp == nil { |
448 | + client.notificationsEndp = bus.SessionBus.Endpoint(notifications.BusAddress, client.log) |
449 | + } |
450 | |
451 | client.connCh = make(chan bool, 1) |
452 | client.sessionConnectedCh = make(chan uint32, 1) |
453 | @@ -160,7 +158,27 @@ |
454 | ExpectAllRepairedTime: client.config.ExpectAllRepairedTime.TimeDuration(), |
455 | PEM: client.pem, |
456 | Info: info, |
457 | - AuthHelper: client.config.AuthHelper, |
458 | + AuthGetter: client.getAuthorization, |
459 | + AuthURL: client.config.SessionURL, |
460 | + } |
461 | +} |
462 | + |
463 | +// getAuthorization gets the authorization blob to send to the server |
464 | +func (client *PushClient) getAuthorization(url string) string { |
465 | + client.log.Debugf("getting authorization for %s", url) |
466 | + // using a helper, for now at least |
467 | + if len(client.config.AuthHelper) == 0 { |
468 | + // do nothing if helper is unset or empty |
469 | + return "" |
470 | + } |
471 | + |
472 | + auth, err := exec.Command(client.config.AuthHelper, url).Output() |
473 | + if err != nil { |
474 | + // For now we just log the error, as we don't want to block unauthorized users |
475 | + client.log.Errorf("unable to get the authorization token from the account: %v", err) |
476 | + return "" |
477 | + } else { |
478 | + return strings.TrimSpace(string(auth)) |
479 | } |
480 | } |
481 | |
482 | @@ -185,12 +203,10 @@ |
483 | go connectivity.ConnectedState(client.connectivityEndp, |
484 | client.config.ConnectivityConfig, client.log, client.connCh) |
485 | iniCh := make(chan uint32) |
486 | - go func() { iniCh <- util.NewAutoRedialer(client.notificationsEndp).Redial() }() |
487 | go func() { iniCh <- util.NewAutoRedialer(client.urlDispatcherEndp).Redial() }() |
488 | go func() { iniCh <- util.NewAutoRedialer(client.systemImageEndp).Redial() }() |
489 | <-iniCh |
490 | <-iniCh |
491 | - <-iniCh |
492 | |
493 | sysimg := systemimage.New(client.systemImageEndp, client.log) |
494 | info, err := sysimg.Info() |
495 | @@ -198,8 +214,11 @@ |
496 | return err |
497 | } |
498 | client.systemImageInfo = info |
499 | + return err |
500 | +} |
501 | |
502 | - actionsCh, err := notifications.Raw(client.notificationsEndp, client.log).WatchActions() |
503 | +func (client *PushClient) takePostalServiceBus() error { |
504 | + actionsCh, err := client.postalService.TakeTheBus() |
505 | client.actionsCh = actionsCh |
506 | return err |
507 | } |
508 | @@ -296,28 +315,12 @@ |
509 | return false |
510 | } |
511 | |
512 | -func (client *PushClient) sendNotification(action_id, icon, summary, body string) (uint32, error) { |
513 | - a := []string{action_id, "Switch to app"} // action value not visible on the phone |
514 | - h := map[string]*dbus.Variant{"x-canonical-switch-to-application": &dbus.Variant{true}} |
515 | - nots := notifications.Raw(client.notificationsEndp, client.log) |
516 | - return nots.Notify( |
517 | - "ubuntu-push-client", // app name |
518 | - uint32(0), // id |
519 | - icon, // icon |
520 | - summary, // summary |
521 | - body, // body |
522 | - a, // actions |
523 | - h, // hints |
524 | - int32(10*1000), // timeout (ms) |
525 | - ) |
526 | -} |
527 | - |
528 | // handleBroadcastNotification deals with receiving a broadcast notification |
529 | func (client *PushClient) handleBroadcastNotification(msg *session.BroadcastNotification) error { |
530 | if !client.filterBroadcastNotification(msg) { |
531 | return nil |
532 | } |
533 | - not_id, err := client.sendNotification(ACTION_ID_BROADCAST, |
534 | + not_id, err := client.postalService.SendNotification(service.ACTION_ID_BROADCAST, |
535 | "update_manager_icon", "There's an updated system image.", |
536 | "Tap to open the system updater.") |
537 | if err != nil { |
538 | @@ -331,7 +334,7 @@ |
539 | // handleUnicastNotification deals with receiving a unicast notification |
540 | func (client *PushClient) handleUnicastNotification(msg *protocol.Notification) error { |
541 | client.log.Debugf("sending notification %#v for %#v.", msg.MsgId, msg.AppId) |
542 | - return client.service.Inject(msg.AppId, string(msg.Payload)) |
543 | + return client.postalService.Inject(msg.AppId, msg.MsgId, string(msg.Payload)) |
544 | } |
545 | |
546 | // handleClick deals with the user clicking a notification |
547 | @@ -342,7 +345,7 @@ |
548 | // |
549 | // From ACM's SIGPLAN publication, (September, 1982), Article |
550 | // "Epigrams in Programming", by Alan J. Perlis of Yale University. |
551 | - url := strings.TrimPrefix(action_id, ACTION_ID_SNOWFLAKE) |
552 | + url := strings.TrimPrefix(action_id, service.ACTION_ID_SNOWFLAKE) |
553 | if len(url) == len(action_id) || len(url) == 0 { |
554 | // it didn't start with the prefix |
555 | return nil |
556 | @@ -392,52 +395,49 @@ |
557 | client.handleErr) |
558 | } |
559 | |
560 | -// these are the currently supported fields of a unicast message |
561 | -type UnicastMessage struct { |
562 | - Icon string `json:"icon"` |
563 | - Body string `json:"body"` |
564 | - Summary string `json:"summary"` |
565 | - URL string `json:"url"` |
566 | - Blob json.RawMessage `json:"blob"` |
567 | -} |
568 | - |
569 | -func (client *PushClient) messageHandler(message []byte) error { |
570 | - var umsg = new(UnicastMessage) |
571 | - err := json.Unmarshal(message, &umsg) |
572 | - if err != nil { |
573 | - client.log.Errorf("unable to unmarshal message: %v", err) |
574 | - return err |
575 | - } |
576 | - |
577 | - not_id, err := client.sendNotification( |
578 | - ACTION_ID_SNOWFLAKE+umsg.URL, |
579 | - umsg.Icon, umsg.Summary, umsg.Body) |
580 | - |
581 | - if err != nil { |
582 | - client.log.Errorf("showing notification: %s", err) |
583 | - return err |
584 | - } |
585 | - client.log.Debugf("got notification id %d", not_id) |
586 | - return nil |
587 | -} |
588 | - |
589 | func (client *PushClient) startService() error { |
590 | - if client.serviceEndpoint == nil { |
591 | - client.serviceEndpoint = bus.SessionBus.Endpoint(service.BusAddress, client.log) |
592 | - } |
593 | - |
594 | - client.service = service.NewService(client.serviceEndpoint, client.log) |
595 | - client.service.SetMessageHandler(client.messageHandler) |
596 | - return client.service.Start() |
597 | + if client.pushServiceEndpoint == nil { |
598 | + client.pushServiceEndpoint = bus.SessionBus.Endpoint(service.PushServiceBusAddress, client.log) |
599 | + } |
600 | + if client.postalServiceEndpoint == nil { |
601 | + client.postalServiceEndpoint = bus.SessionBus.Endpoint(service.PostalServiceBusAddress, client.log) |
602 | + } |
603 | + |
604 | + client.pushService = service.NewPushService(client.pushServiceEndpoint, client.log) |
605 | + client.pushService.SetRegistrationURL(client.config.RegistrationURL) |
606 | + client.pushService.SetAuthGetter(client.getAuthorization) |
607 | + client.pushService.SetDeviceId(client.deviceId) |
608 | + if err := client.pushService.Start(); err != nil { |
609 | + return err |
610 | + } |
611 | + return nil |
612 | +} |
613 | + |
614 | +func (client *PushClient) setupPostalService() error { |
615 | + if client.notificationsEndp == nil { |
616 | + client.notificationsEndp = bus.SessionBus.Endpoint(notifications.BusAddress, client.log) |
617 | + } |
618 | + client.postalService = service.NewPostalService(client.postalServiceEndpoint, client.notificationsEndp, client.log) |
619 | + return nil |
620 | +} |
621 | + |
622 | +func (client *PushClient) startPostalService() error { |
623 | + if err := client.postalService.Start(); err != nil { |
624 | + return err |
625 | + } |
626 | + return nil |
627 | } |
628 | |
629 | // Start calls doStart with the "real" starters |
630 | func (client *PushClient) Start() error { |
631 | return client.doStart( |
632 | client.configure, |
633 | + client.getDeviceId, |
634 | client.startService, |
635 | - client.getDeviceId, |
636 | + client.setupPostalService, |
637 | + client.startPostalService, |
638 | client.takeTheBus, |
639 | + client.takePostalServiceBus, |
640 | client.initSession, |
641 | ) |
642 | } |
643 | |
644 | === modified file 'client/client_test.go' |
645 | --- client/client_test.go 2014-05-20 13:56:37 +0000 |
646 | +++ client/client_test.go 2014-07-02 13:12:36 +0000 |
647 | @@ -37,6 +37,7 @@ |
648 | "launchpad.net/ubuntu-push/bus/notifications" |
649 | "launchpad.net/ubuntu-push/bus/systemimage" |
650 | testibus "launchpad.net/ubuntu-push/bus/testing" |
651 | + "launchpad.net/ubuntu-push/client/service" |
652 | "launchpad.net/ubuntu-push/client/session" |
653 | "launchpad.net/ubuntu-push/client/session/seenstate" |
654 | "launchpad.net/ubuntu-push/config" |
655 | @@ -103,11 +104,13 @@ |
656 | "stabilizing_timeout": "0ms", |
657 | "connectivity_check_url": "", |
658 | "connectivity_check_md5": "", |
659 | - "addr": ":0", |
660 | - "cert_pem_file": pem_file, |
661 | - "recheck_timeout": "3h", |
662 | - "auth_helper": []string{}, |
663 | - "log_level": "debug", |
664 | + "addr": ":0", |
665 | + "cert_pem_file": pem_file, |
666 | + "recheck_timeout": "3h", |
667 | + "auth_helper": "", |
668 | + "session_url": "xyzzy://", |
669 | + "registration_url": "reg://", |
670 | + "log_level": "debug", |
671 | } |
672 | for k, v := range overrides { |
673 | cfgMap[k] = v |
674 | @@ -257,7 +260,7 @@ |
675 | |
676 | func (cs *clientSuite) TestDeriveSessionConfig(c *C) { |
677 | cs.writeTestConfig(map[string]interface{}{ |
678 | - "auth_helper": []string{"auth", "helper"}, |
679 | + "auth_helper": "auth helper", |
680 | }) |
681 | info := map[string]interface{}{ |
682 | "foo": 1, |
683 | @@ -272,7 +275,8 @@ |
684 | ExpectAllRepairedTime: 30 * time.Minute, |
685 | PEM: cli.pem, |
686 | Info: info, |
687 | - AuthHelper: []string{"auth", "helper"}, |
688 | + AuthGetter: func(string) string { return "" }, |
689 | + AuthURL: "xyzzy://", |
690 | } |
691 | // sanity check that we are looking at all fields |
692 | vExpected := reflect.ValueOf(expected) |
693 | @@ -284,6 +288,11 @@ |
694 | } |
695 | // finally compare |
696 | conf := cli.deriveSessionConfig(info) |
697 | + // compare authGetter by string |
698 | + c.Check(fmt.Sprintf("%v", conf.AuthGetter), Equals, fmt.Sprintf("%v", cli.getAuthorization)) |
699 | + // and set it to nil |
700 | + conf.AuthGetter = nil |
701 | + expected.AuthGetter = nil |
702 | c.Check(conf, DeepEquals, expected) |
703 | } |
704 | |
705 | @@ -292,28 +301,54 @@ |
706 | ******************************************************************/ |
707 | |
708 | func (cs *clientSuite) TestStartServiceWorks(c *C) { |
709 | + cs.writeTestConfig(map[string]interface{}{ |
710 | + "auth_helper": helpers.ScriptAbsPath("dummyauth.sh"), |
711 | + }) |
712 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
713 | + cli.configure() |
714 | cli.log = cs.log |
715 | - cli.serviceEndpoint = testibus.NewTestingEndpoint(condition.Work(true), nil) |
716 | - c.Check(cli.service, IsNil) |
717 | + cli.deviceId = "fake-id" |
718 | + cli.pushServiceEndpoint = testibus.NewTestingEndpoint(condition.Work(true), nil) |
719 | + cli.postalServiceEndpoint = testibus.NewTestingEndpoint(condition.Work(true), nil) |
720 | + c.Check(cli.pushService, IsNil) |
721 | c.Check(cli.startService(), IsNil) |
722 | - c.Assert(cli.service, NotNil) |
723 | - c.Check(cli.service.IsRunning(), Equals, true) |
724 | - c.Check(cli.service.GetMessageHandler(), NotNil) |
725 | - cli.service.Stop() |
726 | + c.Assert(cli.pushService, NotNil) |
727 | + c.Check(cli.pushService.IsRunning(), Equals, true) |
728 | + c.Check(cli.pushService.GetDeviceId(), Equals, "fake-id") |
729 | + c.Check(cli.pushService.GetRegistrationAuthorization(), Equals, "hello reg://") |
730 | + c.Assert(cli.setupPostalService(), IsNil) |
731 | + c.Assert(cli.startPostalService(), IsNil) |
732 | + c.Check(cli.postalService.IsRunning(), Equals, true) |
733 | + cli.pushService.Stop() |
734 | + cli.postalService.Stop() |
735 | } |
736 | |
737 | func (cs *clientSuite) TestStartServiceErrorsOnNilLog(c *C) { |
738 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
739 | c.Check(cli.log, IsNil) |
740 | c.Check(cli.startService(), NotNil) |
741 | + c.Assert(cli.setupPostalService(), IsNil) |
742 | + c.Assert(cli.startPostalService(), NotNil) |
743 | } |
744 | |
745 | -func (cs *clientSuite) TestStartServiceErrorsOnBusDialFail(c *C) { |
746 | +func (cs *clientSuite) TestStartServiceErrorsOnBusDialPushFail(c *C) { |
747 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
748 | cli.log = cs.log |
749 | - cli.serviceEndpoint = testibus.NewTestingEndpoint(condition.Work(false), nil) |
750 | + cli.pushServiceEndpoint = testibus.NewTestingEndpoint(condition.Work(false), nil) |
751 | + cli.postalServiceEndpoint = testibus.NewTestingEndpoint(condition.Work(false), nil) |
752 | c.Check(cli.startService(), NotNil) |
753 | + c.Assert(cli.setupPostalService(), IsNil) |
754 | + c.Assert(cli.startPostalService(), NotNil) |
755 | +} |
756 | + |
757 | +func (cs *clientSuite) TestStartServiceErrorsOnBusDialPostalFail(c *C) { |
758 | + cli := NewPushClient(cs.configPath, cs.leveldbPath) |
759 | + cli.log = cs.log |
760 | + cli.pushServiceEndpoint = testibus.NewTestingEndpoint(condition.Work(true), nil) |
761 | + cli.postalServiceEndpoint = testibus.NewTestingEndpoint(condition.Work(false), nil) |
762 | + c.Check(cli.startService(), IsNil) |
763 | + c.Assert(cli.setupPostalService(), IsNil) |
764 | + c.Assert(cli.startPostalService(), NotNil) |
765 | } |
766 | |
767 | /***************************************************************** |
768 | @@ -386,6 +421,8 @@ |
769 | cli.systemImageEndp = siEndp |
770 | |
771 | c.Assert(cli.takeTheBus(), IsNil) |
772 | + c.Assert(cli.setupPostalService(), IsNil) |
773 | + c.Assert(cli.takePostalServiceBus(), IsNil) |
774 | // the notifications and urldispatcher endpoints retried until connected |
775 | c.Check(nCond.OK(), Equals, true) |
776 | c.Check(uCond.OK(), Equals, true) |
777 | @@ -415,6 +452,8 @@ |
778 | cli.systemImageEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false)) |
779 | |
780 | c.Check(cli.takeTheBus(), NotNil) |
781 | + c.Assert(cli.setupPostalService(), IsNil) |
782 | + c.Assert(cli.takePostalServiceBus(), NotNil) |
783 | c.Check(cli.actionsCh, IsNil) |
784 | } |
785 | |
786 | @@ -608,6 +647,8 @@ |
787 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1)) |
788 | cli.notificationsEndp = endp |
789 | cli.log = cs.log |
790 | + cli.postalServiceEndpoint = testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1)) |
791 | + cli.setupPostalService() |
792 | c.Check(cli.handleBroadcastNotification(positiveBroadcastNotification), IsNil) |
793 | // check we sent the notification |
794 | args := testibus.GetCallArgs(endp) |
795 | @@ -635,6 +676,8 @@ |
796 | cli.log = cs.log |
797 | endp := testibus.NewTestingEndpoint(nil, condition.Work(false)) |
798 | cli.notificationsEndp = endp |
799 | + cli.postalServiceEndpoint = testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1)) |
800 | + cli.setupPostalService() |
801 | c.Check(cli.handleBroadcastNotification(positiveBroadcastNotification), NotNil) |
802 | } |
803 | |
804 | @@ -642,19 +685,24 @@ |
805 | handleUnicastNotification tests |
806 | ******************************************************************/ |
807 | |
808 | -var notif = &protocol.Notification{AppId: "hello", Payload: []byte(`{"url": "xyzzy"}`), MsgId: "42"} |
809 | +var payload = `{"message": "aGVsbG8=", "notification": {"card": {"icon": "icon-value", "summary": "summary-value", "body": "body-value", "actions": []}}}` |
810 | +var notif = &protocol.Notification{AppId: "hello", Payload: []byte(payload), MsgId: "42"} |
811 | |
812 | func (cs *clientSuite) TestHandleUcastNotification(c *C) { |
813 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
814 | svcEndp := testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), uint32(1)) |
815 | + postEndp := testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), uint32(1)) |
816 | cli.log = cs.log |
817 | - cli.serviceEndpoint = svcEndp |
818 | + cli.pushServiceEndpoint = svcEndp |
819 | + cli.postalServiceEndpoint = postEndp |
820 | notsEndp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1)) |
821 | cli.notificationsEndp = notsEndp |
822 | c.Assert(cli.startService(), IsNil) |
823 | + c.Assert(cli.setupPostalService(), IsNil) |
824 | + c.Assert(cli.startPostalService(), IsNil) |
825 | c.Check(cli.handleUnicastNotification(notif), IsNil) |
826 | // check we sent the notification |
827 | - args := testibus.GetCallArgs(svcEndp) |
828 | + args := testibus.GetCallArgs(postEndp) |
829 | c.Assert(len(args), Not(Equals), 0) |
830 | c.Check(args[len(args)-1].Member, Equals, "::Signal") |
831 | c.Check(cs.log.Captured(), Matches, `(?m).*sending notification "42" for "hello".*`) |
832 | @@ -675,14 +723,14 @@ |
833 | args := testibus.GetCallArgs(endp) |
834 | c.Assert(args, HasLen, 0) |
835 | // check we worked with the right action id |
836 | - c.Check(cli.handleClick(ACTION_ID_BROADCAST), IsNil) |
837 | + c.Check(cli.handleClick(service.ACTION_ID_BROADCAST), IsNil) |
838 | // check we sent the notification |
839 | args = testibus.GetCallArgs(endp) |
840 | c.Assert(args, HasLen, 1) |
841 | c.Check(args[0].Member, Equals, "DispatchURL") |
842 | - c.Check(args[0].Args, DeepEquals, []interface{}{system_update_url}) |
843 | + c.Check(args[0].Args, DeepEquals, []interface{}{service.SystemUpdateUrl}) |
844 | // check we worked with the right action id |
845 | - c.Check(cli.handleClick(ACTION_ID_SNOWFLAKE+"foo"), IsNil) |
846 | + c.Check(cli.handleClick(service.ACTION_ID_SNOWFLAKE+"foo"), IsNil) |
847 | // check we sent the notification |
848 | args = testibus.GetCallArgs(endp) |
849 | c.Assert(args, HasLen, 2) |
850 | @@ -810,6 +858,8 @@ |
851 | cli.connectivityEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), |
852 | uint32(networkmanager.ConnectedGlobal)) |
853 | cli.systemImageInfo = siInfoRes |
854 | + cli.postalServiceEndpoint = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), uint32(1)) |
855 | + cli.setupPostalService() |
856 | c.Assert(cli.initSession(), IsNil) |
857 | |
858 | cli.session.BroadcastCh = make(chan *session.BroadcastNotification) |
859 | @@ -829,7 +879,7 @@ |
860 | c.Check(cs.log.Captured(), Matches, "(?ms).*Session connected after 42 attempts$") |
861 | |
862 | // * actionsCh to the click handler/url dispatcher |
863 | - aCh <- notifications.RawActionReply{ActionId: ACTION_ID_BROADCAST} |
864 | + aCh <- notifications.RawActionReply{ActionId: service.ACTION_ID_BROADCAST} |
865 | tick() |
866 | uargs := testibus.GetCallArgs(cli.urlDispatcherEndp) |
867 | c.Assert(uargs, HasLen, 1) |
868 | @@ -879,7 +929,7 @@ |
869 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
870 | // before start, everything sucks: |
871 | // no service, |
872 | - c.Check(cli.service, IsNil) |
873 | + c.Check(cli.pushService, IsNil) |
874 | // no config, |
875 | c.Check(string(cli.config.Addr), Equals, "") |
876 | // no device id, |
877 | @@ -904,9 +954,10 @@ |
878 | // and a bus, |
879 | c.Check(cli.notificationsEndp, NotNil) |
880 | // and a service, |
881 | - c.Check(cli.service, NotNil) |
882 | + c.Check(cli.pushService, NotNil) |
883 | // and everthying us just peachy! |
884 | - cli.service.Stop() // cleanup |
885 | + cli.pushService.Stop() // cleanup |
886 | + cli.postalService.Stop() // cleanup |
887 | } |
888 | |
889 | func (cs *clientSuite) TestStartCanFail(c *C) { |
890 | @@ -917,37 +968,41 @@ |
891 | c.Check(err, NotNil) |
892 | } |
893 | |
894 | -func (cs *clientSuite) TestMessageHandler(c *C) { |
895 | - cli := NewPushClient(cs.configPath, cs.leveldbPath) |
896 | - endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1)) |
897 | - cli.notificationsEndp = endp |
898 | - cli.log = cs.log |
899 | - err := cli.messageHandler([]byte(`{"icon": "icon-value", "summary": "summary-value", "body": "body-value"}`)) |
900 | - c.Assert(err, IsNil) |
901 | - args := testibus.GetCallArgs(endp) |
902 | - c.Assert(args, HasLen, 1) |
903 | - c.Check(args[0].Member, Equals, "Notify") |
904 | - c.Check(args[0].Args[0], Equals, "ubuntu-push-client") |
905 | - c.Check(args[0].Args[2], Equals, "icon-value") |
906 | - c.Check(args[0].Args[3], Equals, "summary-value") |
907 | - c.Check(args[0].Args[4], Equals, "body-value") |
908 | -} |
909 | - |
910 | -func (cs *clientSuite) TestMessageHandlerReportsUnmarshalErrors(c *C) { |
911 | - cli := NewPushClient(cs.configPath, cs.leveldbPath) |
912 | - cli.log = cs.log |
913 | - |
914 | - err := cli.messageHandler([]byte(`{"broken`)) |
915 | - c.Check(err, NotNil) |
916 | - c.Check(cs.log.Captured(), Matches, "(?msi).*unable to unmarshal message:.*") |
917 | -} |
918 | - |
919 | -func (cs *clientSuite) TestMessageHandlerReportsFailedNotifies(c *C) { |
920 | - cli := NewPushClient(cs.configPath, cs.leveldbPath) |
921 | - endp := testibus.NewTestingEndpoint(nil, condition.Work(false)) |
922 | - cli.notificationsEndp = endp |
923 | - cli.log = cs.log |
924 | - err := cli.messageHandler([]byte(`{}`)) |
925 | - c.Assert(err, NotNil) |
926 | - c.Check(cs.log.Captured(), Matches, "(?msi).*showing notification: no way$") |
927 | +func (cs *clientSuite) TestInitSessionErr(c *C) { |
928 | + cli := NewPushClient(cs.configPath, cs.leveldbPath) |
929 | + cli.log = cs.log |
930 | + cli.systemImageInfo = siInfoRes |
931 | + // change the cli.pem value so initSession fails |
932 | + cli.pem = []byte("foo") |
933 | + c.Assert(cli.initSession(), NotNil) |
934 | +} |
935 | + |
936 | +/***************************************************************** |
937 | + getAuthorization() tests |
938 | +******************************************************************/ |
939 | + |
940 | +func (cs *clientSuite) TestGetAuthorizationIgnoresErrors(c *C) { |
941 | + cli := NewPushClient(cs.configPath, cs.leveldbPath) |
942 | + cli.configure() |
943 | + cli.config.AuthHelper = "/no/such/executable" |
944 | + |
945 | + c.Check(cli.getAuthorization("xyzzy://"), Equals, "") |
946 | +} |
947 | + |
948 | +func (cs *clientSuite) TestGetAuthorizationGetsIt(c *C) { |
949 | + cli := NewPushClient(cs.configPath, cs.leveldbPath) |
950 | + cli.configure() |
951 | + cli.config.AuthHelper = helpers.ScriptAbsPath("dummyauth.sh") |
952 | + |
953 | + c.Check(cli.getAuthorization("xyzzy://"), Equals, "hello xyzzy://") |
954 | +} |
955 | + |
956 | +func (cs *clientSuite) TestGetAuthorizationWorksIfUnsetOrNil(c *C) { |
957 | + cli := NewPushClient(cs.configPath, cs.leveldbPath) |
958 | + cli.log = cs.log |
959 | + |
960 | + c.Assert(cli.config, NotNil) |
961 | + c.Check(cli.getAuthorization("xyzzy://"), Equals, "") |
962 | + cli.configure() |
963 | + c.Check(cli.getAuthorization("xyzzy://"), Equals, "") |
964 | } |
965 | |
966 | === modified file 'client/gethosts/gethost_test.go' |
967 | --- client/gethosts/gethost_test.go 2014-05-02 10:42:16 +0000 |
968 | +++ client/gethosts/gethost_test.go 2014-07-02 13:12:36 +0000 |
969 | @@ -59,7 +59,7 @@ |
970 | res, err := gh.Get() |
971 | c.Assert(err, IsNil) |
972 | c.Check(*res, DeepEquals, |
973 | - Host{Domain: "example.com", Hosts: []string{"http://c1130408a700afe0"}}) |
974 | + Host{Domain: "example.com", Hosts: []string{"http://bdd2ae7116c85a45"}}) |
975 | } |
976 | |
977 | func (s *getHostsSuite) TestGetTimeout(c *C) { |
978 | |
979 | === added file 'client/service/common.go' |
980 | --- client/service/common.go 1970-01-01 00:00:00 +0000 |
981 | +++ client/service/common.go 2014-07-02 13:12:36 +0000 |
982 | @@ -0,0 +1,99 @@ |
983 | +/* |
984 | + Copyright 2014 Canonical Ltd. |
985 | + |
986 | + This program is free software: you can redistribute it and/or modify it |
987 | + under the terms of the GNU General Public License version 3, as published |
988 | + by the Free Software Foundation. |
989 | + |
990 | + This program is distributed in the hope that it will be useful, but |
991 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
992 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
993 | + PURPOSE. See the GNU General Public License for more details. |
994 | + |
995 | + You should have received a copy of the GNU General Public License along |
996 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
997 | +*/ |
998 | + |
999 | +// package service implements the dbus-level service with which client |
1000 | +// applications are expected to interact. |
1001 | +package service |
1002 | + |
1003 | +import ( |
1004 | + "errors" |
1005 | + "sync" |
1006 | + |
1007 | + "launchpad.net/ubuntu-push/bus" |
1008 | + "launchpad.net/ubuntu-push/logger" |
1009 | +) |
1010 | + |
1011 | +type DBusService struct { |
1012 | + lock sync.RWMutex |
1013 | + state ServiceState |
1014 | + Log logger.Logger |
1015 | + Bus bus.Endpoint |
1016 | +} |
1017 | + |
1018 | +// the service can be in a numnber of states |
1019 | +type ServiceState uint8 |
1020 | + |
1021 | +const ( |
1022 | + StateUnknown ServiceState = iota |
1023 | + StateRunning // Start() has been successfully called |
1024 | + StateFinished // Stop() has been successfully called |
1025 | +) |
1026 | + |
1027 | +var ( |
1028 | + NotConfigured = errors.New("not configured") |
1029 | + AlreadyStarted = errors.New("already started") |
1030 | + BadArgCount = errors.New("Wrong number of arguments") |
1031 | + BadArgType = errors.New("Bad argument type") |
1032 | +) |
1033 | + |
1034 | +// IsRunning() returns whether the service's state is StateRunning |
1035 | +func (svc *DBusService) IsRunning() bool { |
1036 | + svc.lock.RLock() |
1037 | + defer svc.lock.RUnlock() |
1038 | + return svc.state == StateRunning |
1039 | +} |
1040 | + |
1041 | +// Start() dials the bus, grab the name, and listens for method calls. |
1042 | +func (svc *DBusService) Start(dispatchMap bus.DispatchMap, busAddr bus.Address) error { |
1043 | + svc.lock.Lock() |
1044 | + defer svc.lock.Unlock() |
1045 | + if svc.state != StateUnknown { |
1046 | + return AlreadyStarted |
1047 | + } |
1048 | + if svc.Log == nil || svc.Bus == nil { |
1049 | + return NotConfigured |
1050 | + } |
1051 | + err := svc.Bus.Dial() |
1052 | + if err != nil { |
1053 | + return err |
1054 | + } |
1055 | + ch := svc.Bus.GrabName(true) |
1056 | + log := svc.Log |
1057 | + go func() { |
1058 | + for err := range ch { |
1059 | + if !svc.IsRunning() { |
1060 | + break |
1061 | + } |
1062 | + if err != nil { |
1063 | + log.Fatalf("name channel for %s got: %v", |
1064 | + busAddr.Name, err) |
1065 | + } |
1066 | + } |
1067 | + }() |
1068 | + svc.Bus.WatchMethod(dispatchMap, "/*", svc) |
1069 | + svc.state = StateRunning |
1070 | + return nil |
1071 | +} |
1072 | + |
1073 | +// Stop() closes the bus and sets the state to StateFinished |
1074 | +func (svc *DBusService) Stop() { |
1075 | + svc.lock.Lock() |
1076 | + defer svc.lock.Unlock() |
1077 | + if svc.Bus != nil { |
1078 | + svc.Bus.Close() |
1079 | + } |
1080 | + svc.state = StateFinished |
1081 | +} |
1082 | |
1083 | === added file 'client/service/postal.go' |
1084 | --- client/service/postal.go 1970-01-01 00:00:00 +0000 |
1085 | +++ client/service/postal.go 2014-07-02 13:12:36 +0000 |
1086 | @@ -0,0 +1,181 @@ |
1087 | +/* |
1088 | + Copyright 2013-2014 Canonical Ltd. |
1089 | + |
1090 | + This program is free software: you can redistribute it and/or modify it |
1091 | + under the terms of the GNU General Public License version 3, as published |
1092 | + by the Free Software Foundation. |
1093 | + |
1094 | + This program is distributed in the hope that it will be useful, but |
1095 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
1096 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1097 | + PURPOSE. See the GNU General Public License for more details. |
1098 | + |
1099 | + You should have received a copy of the GNU General Public License along |
1100 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
1101 | +*/ |
1102 | + |
1103 | +package service |
1104 | + |
1105 | +import ( |
1106 | + "strings" |
1107 | + |
1108 | + "code.google.com/p/go-uuid/uuid" |
1109 | + "launchpad.net/go-dbus/v1" |
1110 | + |
1111 | + "launchpad.net/ubuntu-push/bus" |
1112 | + "launchpad.net/ubuntu-push/bus/notifications" |
1113 | + "launchpad.net/ubuntu-push/launch_helper" |
1114 | + "launchpad.net/ubuntu-push/logger" |
1115 | + "launchpad.net/ubuntu-push/messaging" |
1116 | + "launchpad.net/ubuntu-push/nih" |
1117 | + "launchpad.net/ubuntu-push/util" |
1118 | +) |
1119 | + |
1120 | +// PostalService is the dbus api |
1121 | +type PostalService struct { |
1122 | + DBusService |
1123 | + mbox map[string][]string |
1124 | + msgHandler func(string, string, *launch_helper.HelperOutput) error |
1125 | + HelperLauncher launch_helper.HelperLauncher |
1126 | + messagingMenu *messaging.MessagingMenu |
1127 | + notificationsEndp bus.Endpoint |
1128 | +} |
1129 | + |
1130 | +var ( |
1131 | + PostalServiceBusAddress = bus.Address{ |
1132 | + Interface: "com.ubuntu.Postal", |
1133 | + Path: "/com/ubuntu/Postal", |
1134 | + Name: "com.ubuntu.Postal", |
1135 | + } |
1136 | +) |
1137 | + |
1138 | +var ( |
1139 | + SystemUpdateUrl = "settings:///system/system-update" |
1140 | + ACTION_ID_SNOWFLAKE = "::ubuntu-push-client::" |
1141 | + ACTION_ID_BROADCAST = ACTION_ID_SNOWFLAKE + SystemUpdateUrl |
1142 | +) |
1143 | + |
1144 | +// NewPostalService() builds a new service and returns it. |
1145 | +func NewPostalService(busEndp bus.Endpoint, notificationsEndp bus.Endpoint, log logger.Logger) *PostalService { |
1146 | + var svc = &PostalService{} |
1147 | + svc.Log = log |
1148 | + svc.Bus = busEndp |
1149 | + svc.messagingMenu = messaging.New(log) |
1150 | + svc.HelperLauncher = launch_helper.NewTrivialHelperLauncher(log) |
1151 | + svc.notificationsEndp = notificationsEndp |
1152 | + svc.msgHandler = svc.messageHandler |
1153 | + return svc |
1154 | +} |
1155 | + |
1156 | +// SetMessageHandler() sets the message-handling callback |
1157 | +func (svc *PostalService) SetMessageHandler(callback func(string, string, *launch_helper.HelperOutput) error) { |
1158 | + svc.lock.RLock() |
1159 | + defer svc.lock.RUnlock() |
1160 | + svc.msgHandler = callback |
1161 | +} |
1162 | + |
1163 | +// GetMessageHandler() returns the (possibly nil) messaging handler callback |
1164 | +func (svc *PostalService) GetMessageHandler() func(string, string, *launch_helper.HelperOutput) error { |
1165 | + svc.lock.RLock() |
1166 | + defer svc.lock.RUnlock() |
1167 | + return svc.msgHandler |
1168 | +} |
1169 | + |
1170 | +// Start() dials the bus, grab the name, and listens for method calls. |
1171 | +func (svc *PostalService) Start() error { |
1172 | + return svc.DBusService.Start(bus.DispatchMap{ |
1173 | + "Notifications": svc.notifications, |
1174 | + "Inject": svc.inject, |
1175 | + }, PostalServiceBusAddress) |
1176 | +} |
1177 | + |
1178 | +func (svc *PostalService) TakeTheBus() (<-chan notifications.RawActionReply, error) { |
1179 | + iniCh := make(chan uint32) |
1180 | + go func() { iniCh <- util.NewAutoRedialer(svc.notificationsEndp).Redial() }() |
1181 | + <-iniCh |
1182 | + actionsCh, err := notifications.Raw(svc.notificationsEndp, svc.Log).WatchActions() |
1183 | + return actionsCh, err |
1184 | +} |
1185 | + |
1186 | +func (svc *PostalService) notifications(path string, args, _ []interface{}) ([]interface{}, error) { |
1187 | + if len(args) != 0 { |
1188 | + return nil, BadArgCount |
1189 | + } |
1190 | + appname := string(nih.Unquote([]byte(path[strings.LastIndex(path, "/")+1:]))) |
1191 | + |
1192 | + svc.lock.Lock() |
1193 | + defer svc.lock.Unlock() |
1194 | + |
1195 | + if svc.mbox == nil { |
1196 | + return []interface{}{[]string(nil)}, nil |
1197 | + } |
1198 | + msgs := svc.mbox[appname] |
1199 | + delete(svc.mbox, appname) |
1200 | + |
1201 | + return []interface{}{msgs}, nil |
1202 | +} |
1203 | + |
1204 | +var newNid = uuid.New |
1205 | + |
1206 | +func (svc *PostalService) inject(path string, args, _ []interface{}) ([]interface{}, error) { |
1207 | + if len(args) != 1 { |
1208 | + return nil, BadArgCount |
1209 | + } |
1210 | + notif, ok := args[0].(string) |
1211 | + if !ok { |
1212 | + return nil, BadArgType |
1213 | + } |
1214 | + appname := string(nih.Unquote([]byte(path[strings.LastIndex(path, "/")+1:]))) |
1215 | + |
1216 | + nid := newNid() |
1217 | + |
1218 | + return nil, svc.Inject(appname, nid, notif) |
1219 | +} |
1220 | + |
1221 | +// Inject() signals to an application over dbus that a notification |
1222 | +// has arrived. |
1223 | +func (svc *PostalService) Inject(appname string, nid string, notif string) error { |
1224 | + svc.lock.Lock() |
1225 | + defer svc.lock.Unlock() |
1226 | + if svc.mbox == nil { |
1227 | + svc.mbox = make(map[string][]string) |
1228 | + } |
1229 | + output := svc.HelperLauncher.Run(appname, []byte(notif)) |
1230 | + // XXX also track the nid in the mbox |
1231 | + svc.mbox[appname] = append(svc.mbox[appname], string(output.Message)) |
1232 | + |
1233 | + if svc.msgHandler != nil { |
1234 | + err := svc.msgHandler(appname, nid, output) |
1235 | + if err != nil { |
1236 | + svc.DBusService.Log.Errorf("msgHandler returned %v", err) |
1237 | + return err |
1238 | + } |
1239 | + svc.DBusService.Log.Debugf("call to msgHandler successful") |
1240 | + } |
1241 | + |
1242 | + return svc.Bus.Signal("Notification", "/"+string(nih.Quote([]byte(appname))), []interface{}{appname}) |
1243 | +} |
1244 | + |
1245 | +func (svc *PostalService) messageHandler(appname string, nid string, output *launch_helper.HelperOutput) error { |
1246 | + svc.messagingMenu.Present(appname, nid, output.Notification) |
1247 | + nots := notifications.Raw(svc.notificationsEndp, svc.Log) |
1248 | + _, err := nots.Present(appname, nid, output.Notification) |
1249 | + |
1250 | + return err |
1251 | +} |
1252 | + |
1253 | +func (svc *PostalService) SendNotification(action_id, icon, summary, body string) (uint32, error) { |
1254 | + a := []string{action_id, "Switch to app"} // action value not visible on the phone |
1255 | + h := map[string]*dbus.Variant{"x-canonical-switch-to-application": &dbus.Variant{true}} |
1256 | + nots := notifications.Raw(svc.notificationsEndp, svc.Log) |
1257 | + return nots.Notify( |
1258 | + "ubuntu-push-client", // app name |
1259 | + uint32(0), // id |
1260 | + icon, // icon |
1261 | + summary, // summary |
1262 | + body, // body |
1263 | + a, // actions |
1264 | + h, // hints |
1265 | + int32(10*1000), // timeout (ms) |
1266 | + ) |
1267 | +} |
1268 | |
1269 | === added file 'client/service/postal_test.go' |
1270 | --- client/service/postal_test.go 1970-01-01 00:00:00 +0000 |
1271 | +++ client/service/postal_test.go 2014-07-02 13:12:36 +0000 |
1272 | @@ -0,0 +1,252 @@ |
1273 | +/* |
1274 | + Copyright 2014 Canonical Ltd. |
1275 | + |
1276 | + This program is free software: you can redistribute it and/or modify it |
1277 | + under the terms of the GNU General Public License version 3, as published |
1278 | + by the Free Software Foundation. |
1279 | + |
1280 | + This program is distributed in the hope that it will be useful, but |
1281 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
1282 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1283 | + PURPOSE. See the GNU General Public License for more details. |
1284 | + |
1285 | + You should have received a copy of the GNU General Public License along |
1286 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
1287 | +*/ |
1288 | + |
1289 | +package service |
1290 | + |
1291 | +import ( |
1292 | + "errors" |
1293 | + |
1294 | + . "launchpad.net/gocheck" |
1295 | + |
1296 | + "launchpad.net/ubuntu-push/bus" |
1297 | + testibus "launchpad.net/ubuntu-push/bus/testing" |
1298 | + "launchpad.net/ubuntu-push/launch_helper" |
1299 | + helpers "launchpad.net/ubuntu-push/testing" |
1300 | + "launchpad.net/ubuntu-push/testing/condition" |
1301 | +) |
1302 | + |
1303 | +type postalSuite struct { |
1304 | + log *helpers.TestLogger |
1305 | + bus bus.Endpoint |
1306 | + notifBus bus.Endpoint |
1307 | +} |
1308 | + |
1309 | +var _ = Suite(&postalSuite{}) |
1310 | + |
1311 | +func (ss *postalSuite) SetUpTest(c *C) { |
1312 | + ss.log = helpers.NewTestLogger(c, "debug") |
1313 | + ss.bus = testibus.NewTestingEndpoint(condition.Work(true), nil) |
1314 | + ss.notifBus = testibus.NewTestingEndpoint(condition.Work(true), nil) |
1315 | +} |
1316 | + |
1317 | +func (ss *postalSuite) TestStart(c *C) { |
1318 | + svc := NewPostalService(ss.bus, ss.notifBus, ss.log) |
1319 | + c.Check(svc.IsRunning(), Equals, false) |
1320 | + c.Check(svc.Start(), IsNil) |
1321 | + c.Check(svc.IsRunning(), Equals, true) |
1322 | + svc.Stop() |
1323 | +} |
1324 | + |
1325 | +func (ss *postalSuite) TestStartTwice(c *C) { |
1326 | + svc := NewPostalService(ss.bus, ss.notifBus, ss.log) |
1327 | + c.Check(svc.Start(), IsNil) |
1328 | + c.Check(svc.Start(), Equals, AlreadyStarted) |
1329 | + svc.Stop() |
1330 | +} |
1331 | + |
1332 | +func (ss *postalSuite) TestStartNoLog(c *C) { |
1333 | + svc := NewPostalService(ss.bus, ss.notifBus, nil) |
1334 | + c.Check(svc.Start(), Equals, NotConfigured) |
1335 | +} |
1336 | + |
1337 | +func (ss *postalSuite) TestStartNoBus(c *C) { |
1338 | + svc := NewPostalService(nil, ss.notifBus, ss.log) |
1339 | + c.Check(svc.Start(), Equals, NotConfigured) |
1340 | +} |
1341 | + |
1342 | +func (ss *postalSuite) TestTakeTheBustFail(c *C) { |
1343 | + nEndp := testibus.NewMultiValuedTestingEndpoint(condition.Work(true), condition.Work(false), []interface{}{uint32(1), "hello"}) |
1344 | + svc := NewPostalService(ss.bus, nEndp, ss.log) |
1345 | + _, err := svc.TakeTheBus() |
1346 | + c.Check(err, NotNil) |
1347 | +} |
1348 | + |
1349 | +func (ss *postalSuite) TestTakeTheBustOk(c *C) { |
1350 | + nEndp := testibus.NewMultiValuedTestingEndpoint(condition.Work(true), condition.Work(true), []interface{}{uint32(1), "hello"}) |
1351 | + svc := NewPostalService(ss.bus, nEndp, ss.log) |
1352 | + _, err := svc.TakeTheBus() |
1353 | + c.Check(err, IsNil) |
1354 | +} |
1355 | + |
1356 | +func (ss *postalSuite) TestStartFailsOnBusDialFailure(c *C) { |
1357 | + bus := testibus.NewTestingEndpoint(condition.Work(false), nil) |
1358 | + svc := NewPostalService(bus, ss.notifBus, ss.log) |
1359 | + c.Check(svc.Start(), ErrorMatches, `.*(?i)cond said no.*`) |
1360 | + svc.Stop() |
1361 | +} |
1362 | + |
1363 | +func (ss *postalSuite) TestStartGrabsName(c *C) { |
1364 | + svc := NewPostalService(ss.bus, ss.notifBus, ss.log) |
1365 | + c.Assert(svc.Start(), IsNil) |
1366 | + callArgs := testibus.GetCallArgs(ss.bus) |
1367 | + defer svc.Stop() |
1368 | + c.Assert(callArgs, NotNil) |
1369 | + c.Check(callArgs[0].Member, Equals, "::GrabName") |
1370 | +} |
1371 | + |
1372 | +func (ss *postalSuite) TestStopClosesBus(c *C) { |
1373 | + svc := NewPostalService(ss.bus, ss.notifBus, ss.log) |
1374 | + c.Assert(svc.Start(), IsNil) |
1375 | + svc.Stop() |
1376 | + callArgs := testibus.GetCallArgs(ss.bus) |
1377 | + c.Assert(callArgs, NotNil) |
1378 | + c.Check(callArgs[len(callArgs)-1].Member, Equals, "::Close") |
1379 | +} |
1380 | + |
1381 | +// |
1382 | +// Injection tests |
1383 | + |
1384 | +func (ss *postalSuite) TestInjectWorks(c *C) { |
1385 | + svc := NewPostalService(ss.bus, ss.notifBus, ss.log) |
1386 | + svc.msgHandler = nil |
1387 | + rvs, err := svc.inject("/hello", []interface{}{"world"}, nil) |
1388 | + c.Assert(err, IsNil) |
1389 | + c.Check(rvs, IsNil) |
1390 | + rvs, err = svc.inject("/hello", []interface{}{"there"}, nil) |
1391 | + c.Assert(err, IsNil) |
1392 | + c.Check(rvs, IsNil) |
1393 | + c.Assert(svc.mbox, HasLen, 1) |
1394 | + c.Assert(svc.mbox["hello"], HasLen, 2) |
1395 | + c.Check(svc.mbox["hello"][0], Equals, "world") |
1396 | + c.Check(svc.mbox["hello"][1], Equals, "there") |
1397 | + |
1398 | + // and check it fired the right signal (twice) |
1399 | + callArgs := testibus.GetCallArgs(ss.bus) |
1400 | + c.Assert(callArgs, HasLen, 2) |
1401 | + c.Check(callArgs[0].Member, Equals, "::Signal") |
1402 | + c.Check(callArgs[0].Args, DeepEquals, []interface{}{"Notification", "/hello", []interface{}{"hello"}}) |
1403 | + c.Check(callArgs[1], DeepEquals, callArgs[0]) |
1404 | +} |
1405 | + |
1406 | +func (ss *postalSuite) TestInjectFailsIfInjectFails(c *C) { |
1407 | + bus := testibus.NewTestingEndpoint(condition.Work(true), |
1408 | + condition.Work(false)) |
1409 | + svc := NewPostalService(bus, ss.notifBus, ss.log) |
1410 | + svc.SetMessageHandler(func(string, string, *launch_helper.HelperOutput) error { return errors.New("fail") }) |
1411 | + _, err := svc.inject("/hello", []interface{}{"xyzzy"}, nil) |
1412 | + c.Check(err, NotNil) |
1413 | +} |
1414 | + |
1415 | +func (ss *postalSuite) TestInjectFailsIfBadArgs(c *C) { |
1416 | + for i, s := range []struct { |
1417 | + args []interface{} |
1418 | + errt error |
1419 | + }{ |
1420 | + {nil, BadArgCount}, |
1421 | + {[]interface{}{}, BadArgCount}, |
1422 | + {[]interface{}{1}, BadArgType}, |
1423 | + {[]interface{}{1, 2}, BadArgCount}, |
1424 | + } { |
1425 | + reg, err := new(PostalService).inject("", s.args, nil) |
1426 | + c.Check(reg, IsNil, Commentf("iteration #%d", i)) |
1427 | + c.Check(err, Equals, s.errt, Commentf("iteration #%d", i)) |
1428 | + } |
1429 | +} |
1430 | + |
1431 | +// |
1432 | +// Notifications tests |
1433 | +func (ss *postalSuite) TestNotificationsWorks(c *C) { |
1434 | + svc := NewPostalService(ss.bus, ss.notifBus, ss.log) |
1435 | + nots, err := svc.notifications("/hello", nil, nil) |
1436 | + c.Assert(err, IsNil) |
1437 | + c.Assert(nots, NotNil) |
1438 | + c.Assert(nots, HasLen, 1) |
1439 | + c.Check(nots[0], HasLen, 0) |
1440 | + if svc.mbox == nil { |
1441 | + svc.mbox = make(map[string][]string) |
1442 | + } |
1443 | + svc.mbox["hello"] = append(svc.mbox["hello"], "this", "thing") |
1444 | + nots, err = svc.notifications("/hello", nil, nil) |
1445 | + c.Assert(err, IsNil) |
1446 | + c.Assert(nots, NotNil) |
1447 | + c.Assert(nots, HasLen, 1) |
1448 | + c.Check(nots[0], DeepEquals, []string{"this", "thing"}) |
1449 | +} |
1450 | + |
1451 | +func (ss *postalSuite) TestNotificationsFailsIfBadArgs(c *C) { |
1452 | + reg, err := new(PostalService).notifications("/foo", []interface{}{1}, nil) |
1453 | + c.Check(reg, IsNil) |
1454 | + c.Check(err, Equals, BadArgCount) |
1455 | +} |
1456 | + |
1457 | +func (ss *postalSuite) TestMessageHandlerPublicAPI(c *C) { |
1458 | + svc := new(PostalService) |
1459 | + c.Assert(svc.msgHandler, IsNil) |
1460 | + var ext = &launch_helper.HelperOutput{} |
1461 | + e := errors.New("Hello") |
1462 | + f := func(app string, nid string, s *launch_helper.HelperOutput) error { ext = s; return e } |
1463 | + c.Check(svc.GetMessageHandler(), IsNil) |
1464 | + svc.SetMessageHandler(f) |
1465 | + c.Check(svc.GetMessageHandler(), NotNil) |
1466 | + hOutput := &launch_helper.HelperOutput{[]byte("37"), nil} |
1467 | + c.Check(svc.msgHandler("", "", hOutput), Equals, e) |
1468 | + c.Check(ext, DeepEquals, hOutput) |
1469 | +} |
1470 | + |
1471 | +func (ss *postalSuite) TestInjectCallsMessageHandler(c *C) { |
1472 | + var ext = &launch_helper.HelperOutput{} |
1473 | + svc := NewPostalService(ss.bus, ss.notifBus, ss.log) |
1474 | + f := func(app string, nid string, s *launch_helper.HelperOutput) error { ext = s; return nil } |
1475 | + svc.SetMessageHandler(f) |
1476 | + c.Check(svc.Inject("stuff", "thing", "{}"), IsNil) |
1477 | + c.Check(ext, DeepEquals, &launch_helper.HelperOutput{}) |
1478 | + err := errors.New("ouch") |
1479 | + svc.SetMessageHandler(func(string, string, *launch_helper.HelperOutput) error { return err }) |
1480 | + c.Check(svc.Inject("stuff", "", "{}"), Equals, err) |
1481 | +} |
1482 | + |
1483 | +func (ss *postalSuite) TestMessageHandler(c *C) { |
1484 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1)) |
1485 | + svc := NewPostalService(ss.bus, endp, ss.log) |
1486 | + card := &launch_helper.Card{Icon: "icon-value", Summary: "summary-value", Body: "body-value", Popup: true} |
1487 | + output := &launch_helper.HelperOutput{Notification: &launch_helper.Notification{Card: card}} |
1488 | + err := svc.messageHandler("xyzzy", "", output) |
1489 | + c.Assert(err, IsNil) |
1490 | + args := testibus.GetCallArgs(endp) |
1491 | + c.Assert(args, HasLen, 1) |
1492 | + c.Check(args[0].Member, Equals, "Notify") |
1493 | + c.Check(args[0].Args[0], Equals, "xyzzy") |
1494 | + c.Check(args[0].Args[2], Equals, "icon-value") |
1495 | + c.Check(args[0].Args[3], Equals, "summary-value") |
1496 | + c.Check(args[0].Args[4], Equals, "body-value") |
1497 | +} |
1498 | + |
1499 | +func (ss *postalSuite) TestMessageHandlerReportsFailedNotifies(c *C) { |
1500 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(false)) |
1501 | + svc := NewPostalService(ss.bus, endp, ss.log) |
1502 | + card := &launch_helper.Card{Icon: "icon-value", Summary: "summary-value", Body: "body-value", Popup: true} |
1503 | + notif := &launch_helper.Notification{Card: card} |
1504 | + output := &launch_helper.HelperOutput{Notification: notif} |
1505 | + err := svc.messageHandler("", "", output) |
1506 | + c.Assert(err, NotNil) |
1507 | +} |
1508 | + |
1509 | +func (ss *postalSuite) TestMessageHandlerReportsButIgnoresUnmarshalErrors(c *C) { |
1510 | + svc := NewPostalService(ss.bus, ss.notifBus, ss.log) |
1511 | + output := &launch_helper.HelperOutput{[]byte(`broken`), nil} |
1512 | + err := svc.messageHandler("", "", output) |
1513 | + c.Check(err, IsNil) |
1514 | + c.Check(ss.log.Captured(), Matches, "(?msi).*skipping notification: nil.*") |
1515 | +} |
1516 | + |
1517 | +func (ss *postalSuite) TestMessageHandlerReportsButIgnoresNilNotifies(c *C) { |
1518 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(false)) |
1519 | + svc := NewPostalService(ss.bus, endp, ss.log) |
1520 | + output := &launch_helper.HelperOutput{[]byte(`{}`), nil} |
1521 | + err := svc.messageHandler("", "", output) |
1522 | + c.Assert(err, IsNil) |
1523 | + c.Check(ss.log.Captured(), Matches, "(?msi).*skipping notification: nil.*") |
1524 | +} |
1525 | |
1526 | === modified file 'client/service/service.go' |
1527 | --- client/service/service.go 2014-05-21 10:03:18 +0000 |
1528 | +++ client/service/service.go 2014-07-02 13:12:36 +0000 |
1529 | @@ -14,196 +14,184 @@ |
1530 | with this program. If not, see <http://www.gnu.org/licenses/>. |
1531 | */ |
1532 | |
1533 | -// package service implements the dbus-level service with which client |
1534 | -// applications are expected to interact. |
1535 | package service |
1536 | |
1537 | import ( |
1538 | + "bytes" |
1539 | + "encoding/json" |
1540 | "errors" |
1541 | + "fmt" |
1542 | + "io/ioutil" |
1543 | + "net/http" |
1544 | "os" |
1545 | - "sync" |
1546 | + "strings" |
1547 | |
1548 | "launchpad.net/ubuntu-push/bus" |
1549 | + http13 "launchpad.net/ubuntu-push/http13client" |
1550 | "launchpad.net/ubuntu-push/logger" |
1551 | + "launchpad.net/ubuntu-push/nih" |
1552 | ) |
1553 | |
1554 | -// Service is the dbus api |
1555 | -type Service struct { |
1556 | - lock sync.RWMutex |
1557 | - state ServiceState |
1558 | - mbox map[string][]string |
1559 | - msgHandler func([]byte) error |
1560 | - Log logger.Logger |
1561 | - Bus bus.Endpoint |
1562 | +// PushService is the dbus api |
1563 | +type PushService struct { |
1564 | + DBusService |
1565 | + regURL string |
1566 | + deviceId string |
1567 | + authGetter func(string) string |
1568 | + httpCli http13.Client |
1569 | } |
1570 | |
1571 | -// the service can be in a numnber of states |
1572 | -type ServiceState uint8 |
1573 | - |
1574 | -const ( |
1575 | - StateUnknown ServiceState = iota |
1576 | - StateRunning // Start() has been successfully called |
1577 | - StateFinished // Stop() has been successfully called |
1578 | -) |
1579 | - |
1580 | var ( |
1581 | - NotConfigured = errors.New("not configured") |
1582 | - AlreadyStarted = errors.New("already started") |
1583 | - BusAddress = bus.Address{ |
1584 | + PushServiceBusAddress = bus.Address{ |
1585 | Interface: "com.ubuntu.PushNotifications", |
1586 | Path: "/com/ubuntu/PushNotifications", |
1587 | Name: "com.ubuntu.PushNotifications", |
1588 | } |
1589 | ) |
1590 | |
1591 | -// NewService() builds a new service and returns it. |
1592 | -func NewService(bus bus.Endpoint, log logger.Logger) *Service { |
1593 | - return &Service{Log: log, Bus: bus} |
1594 | -} |
1595 | - |
1596 | -// SetMessageHandler() sets the message-handling callback |
1597 | -func (svc *Service) SetMessageHandler(callback func([]byte) error) { |
1598 | - svc.lock.Lock() |
1599 | - defer svc.lock.Unlock() |
1600 | - svc.msgHandler = callback |
1601 | -} |
1602 | - |
1603 | -// GetMessageHandler() returns the (possibly nil) messaging handler callback |
1604 | -func (svc *Service) GetMessageHandler() func([]byte) error { |
1605 | - svc.lock.RLock() |
1606 | - defer svc.lock.RUnlock() |
1607 | - return svc.msgHandler |
1608 | -} |
1609 | - |
1610 | -// IsRunning() returns whether the service's state is StateRunning |
1611 | -func (svc *Service) IsRunning() bool { |
1612 | - svc.lock.RLock() |
1613 | - defer svc.lock.RUnlock() |
1614 | - return svc.state == StateRunning |
1615 | -} |
1616 | - |
1617 | -// Start() dials the bus, grab the name, and listens for method calls. |
1618 | -func (svc *Service) Start() error { |
1619 | - svc.lock.Lock() |
1620 | - defer svc.lock.Unlock() |
1621 | - if svc.state != StateUnknown { |
1622 | - return AlreadyStarted |
1623 | - } |
1624 | - if svc.Log == nil || svc.Bus == nil { |
1625 | - return NotConfigured |
1626 | - } |
1627 | - err := svc.Bus.Dial() |
1628 | - if err != nil { |
1629 | - return err |
1630 | - } |
1631 | - ch := svc.Bus.GrabName(true) |
1632 | - log := svc.Log |
1633 | - go func() { |
1634 | - for err := range ch { |
1635 | - if !svc.IsRunning() { |
1636 | - break |
1637 | - } |
1638 | - if err != nil { |
1639 | - log.Fatalf("name channel for %s got: %v", |
1640 | - BusAddress.Name, err) |
1641 | - } |
1642 | - } |
1643 | - }() |
1644 | - svc.Bus.WatchMethod(bus.DispatchMap{ |
1645 | - "Register": svc.register, |
1646 | - "Notifications": svc.notifications, |
1647 | - "Inject": svc.inject, |
1648 | - }, svc) |
1649 | - svc.state = StateRunning |
1650 | - return nil |
1651 | -} |
1652 | - |
1653 | -// Stop() closes the bus and sets the state to StateFinished |
1654 | -func (svc *Service) Stop() { |
1655 | - svc.lock.Lock() |
1656 | - defer svc.lock.Unlock() |
1657 | - if svc.Bus != nil { |
1658 | - svc.Bus.Close() |
1659 | - } |
1660 | - svc.state = StateFinished |
1661 | +// NewPushService() builds a new service and returns it. |
1662 | +func NewPushService(bus bus.Endpoint, log logger.Logger) *PushService { |
1663 | + var svc = &PushService{} |
1664 | + svc.Log = log |
1665 | + svc.Bus = bus |
1666 | + return svc |
1667 | +} |
1668 | + |
1669 | +// SetRegistrationURL() sets the registration url for the service |
1670 | +func (svc *PushService) SetRegistrationURL(url string) { |
1671 | + svc.lock.Lock() |
1672 | + defer svc.lock.Unlock() |
1673 | + svc.regURL = url |
1674 | +} |
1675 | + |
1676 | +// SetAuthGetter() sets the authorization getter for the service |
1677 | +func (svc *PushService) SetAuthGetter(authGetter func(string) string) { |
1678 | + svc.lock.Lock() |
1679 | + defer svc.lock.Unlock() |
1680 | + svc.authGetter = authGetter |
1681 | +} |
1682 | + |
1683 | +// getRegistrationAuthorization() returns the authorization header for |
1684 | +// POSTing to the registration HTTP endpoint |
1685 | +// |
1686 | +// (this is for calling with the lock held) |
1687 | +func (svc *PushService) getRegistrationAuthorization() string { |
1688 | + if svc.authGetter != nil && svc.regURL != "" { |
1689 | + return svc.authGetter(svc.regURL) |
1690 | + } else { |
1691 | + return "" |
1692 | + } |
1693 | +} |
1694 | + |
1695 | +// GetRegistrationAuthorization() returns the authorization header for |
1696 | +// POSTing to the registration HTTP endpoint |
1697 | +func (svc *PushService) GetRegistrationAuthorization() string { |
1698 | + svc.lock.RLock() |
1699 | + defer svc.lock.RUnlock() |
1700 | + return svc.getRegistrationAuthorization() |
1701 | +} |
1702 | + |
1703 | +// SetDeviceId() sets the device id |
1704 | +func (svc *PushService) SetDeviceId(deviceId string) { |
1705 | + svc.lock.Lock() |
1706 | + defer svc.lock.Unlock() |
1707 | + svc.deviceId = deviceId |
1708 | +} |
1709 | + |
1710 | +// GetDeviceId() returns the device id |
1711 | +func (svc *PushService) GetDeviceId() string { |
1712 | + svc.lock.RLock() |
1713 | + defer svc.lock.RUnlock() |
1714 | + return svc.deviceId |
1715 | +} |
1716 | + |
1717 | +func (svc *PushService) Start() error { |
1718 | + return svc.DBusService.Start(bus.DispatchMap{ |
1719 | + "Register": svc.register, |
1720 | + }, PushServiceBusAddress) |
1721 | } |
1722 | |
1723 | var ( |
1724 | - BadArgCount = errors.New("Wrong number of arguments") |
1725 | - BadArgType = errors.New("Bad argument type") |
1726 | + BadServer = errors.New("Bad server") |
1727 | + BadRequest = errors.New("Bad request") |
1728 | + BadToken = errors.New("Bad token") |
1729 | + BadAuth = errors.New("Bad auth") |
1730 | ) |
1731 | |
1732 | -func (svc *Service) register(args []interface{}, _ []interface{}) ([]interface{}, error) { |
1733 | - if len(args) != 1 { |
1734 | - return nil, BadArgCount |
1735 | - } |
1736 | - appname, ok := args[0].(string) |
1737 | - if !ok { |
1738 | - return nil, BadArgType |
1739 | - } |
1740 | - |
1741 | - rv := os.Getenv("PUSH_REG_" + appname) |
1742 | - if rv == "" { |
1743 | - rv = "this-is-an-opaque-block-of-random-bits-i-promise" |
1744 | - } |
1745 | - |
1746 | - return []interface{}{rv}, nil |
1747 | -} |
1748 | - |
1749 | -func (svc *Service) notifications(args []interface{}, _ []interface{}) ([]interface{}, error) { |
1750 | - if len(args) != 1 { |
1751 | - return nil, BadArgCount |
1752 | - } |
1753 | - appname, ok := args[0].(string) |
1754 | - if !ok { |
1755 | - return nil, BadArgType |
1756 | - } |
1757 | - |
1758 | - svc.lock.Lock() |
1759 | - defer svc.lock.Unlock() |
1760 | - |
1761 | - if svc.mbox == nil { |
1762 | - return []interface{}{[]string(nil)}, nil |
1763 | - } |
1764 | - msgs := svc.mbox[appname] |
1765 | - delete(svc.mbox, appname) |
1766 | - |
1767 | - return []interface{}{msgs}, nil |
1768 | -} |
1769 | - |
1770 | -func (svc *Service) inject(args []interface{}, _ []interface{}) ([]interface{}, error) { |
1771 | - if len(args) != 2 { |
1772 | - return nil, BadArgCount |
1773 | - } |
1774 | - appname, ok := args[0].(string) |
1775 | - if !ok { |
1776 | - return nil, BadArgType |
1777 | - } |
1778 | - notif, ok := args[1].(string) |
1779 | - if !ok { |
1780 | - return nil, BadArgType |
1781 | - } |
1782 | - |
1783 | - return nil, svc.Inject(appname, notif) |
1784 | -} |
1785 | - |
1786 | -// Inject() signals to an application over dbus that a notification |
1787 | -// has arrived. |
1788 | -func (svc *Service) Inject(appname string, notif string) error { |
1789 | - svc.lock.Lock() |
1790 | - defer svc.lock.Unlock() |
1791 | - if svc.mbox == nil { |
1792 | - svc.mbox = make(map[string][]string) |
1793 | - } |
1794 | - svc.mbox[appname] = append(svc.mbox[appname], notif) |
1795 | - if svc.msgHandler != nil { |
1796 | - err := svc.msgHandler([]byte(notif)) |
1797 | - if err != nil { |
1798 | - svc.Log.Errorf("msgHandler returned %v", err) |
1799 | - return err |
1800 | +type registrationRequest struct { |
1801 | + DeviceId string `json:"deviceid"` |
1802 | + AppId string `json:"appid"` |
1803 | +} |
1804 | + |
1805 | +type registrationReply struct { |
1806 | + Token string `json:"token"` // the bit we're after |
1807 | + Ok bool `json:"ok"` // only ever true or absent |
1808 | + Error string `json:"error"` // these two only used for debugging |
1809 | + Message string `json:"message"` // |
1810 | +} |
1811 | + |
1812 | +func (svc *PushService) register(path string, args, _ []interface{}) ([]interface{}, error) { |
1813 | + svc.lock.RLock() |
1814 | + defer svc.lock.RUnlock() |
1815 | + if len(args) != 0 { |
1816 | + return nil, BadArgCount |
1817 | + } |
1818 | + raw_appname := path[strings.LastIndex(path, "/")+1:] |
1819 | + appname := string(nih.Unquote([]byte(raw_appname))) |
1820 | + |
1821 | + rv := os.Getenv("PUSH_REG_" + raw_appname) |
1822 | + if rv != "" { |
1823 | + return []interface{}{rv}, nil |
1824 | + } |
1825 | + |
1826 | + req_body, err := json.Marshal(registrationRequest{svc.deviceId, appname}) |
1827 | + if err != nil { |
1828 | + return nil, fmt.Errorf("unable to marshal register request body: %v", err) |
1829 | + } |
1830 | + req, err := http13.NewRequest("POST", svc.regURL, bytes.NewReader(req_body)) |
1831 | + if err != nil { |
1832 | + return nil, fmt.Errorf("unable to build register request: %v", err) |
1833 | + } |
1834 | + auth := svc.getRegistrationAuthorization() |
1835 | + if auth == "" { |
1836 | + return nil, BadAuth |
1837 | + } |
1838 | + req.Header.Add("Authorization", auth) |
1839 | + req.Header.Add("Content-Type", "application/json") |
1840 | + |
1841 | + resp, err := svc.httpCli.Do(req) |
1842 | + if err != nil { |
1843 | + return nil, fmt.Errorf("unable to request registration: %v", err) |
1844 | + } |
1845 | + defer resp.Body.Close() |
1846 | + if resp.StatusCode != http.StatusOK { |
1847 | + svc.Log.Errorf("register endpoint replied %d", resp.StatusCode) |
1848 | + switch { |
1849 | + case resp.StatusCode >= http.StatusInternalServerError: |
1850 | + // XXX retry on 503 |
1851 | + return nil, BadServer |
1852 | + default: |
1853 | + return nil, BadRequest |
1854 | } |
1855 | - svc.Log.Debugf("call to msgHandler successful") |
1856 | - } |
1857 | - |
1858 | - return svc.Bus.Signal("Notification", []interface{}{appname}) |
1859 | + } |
1860 | + // errors below here Can't Happen (tm). |
1861 | + body, err := ioutil.ReadAll(resp.Body) |
1862 | + if err != nil { |
1863 | + svc.Log.Errorf("Reading response body: %v", err) |
1864 | + return nil, err |
1865 | + } |
1866 | + |
1867 | + var reply registrationReply |
1868 | + err = json.Unmarshal(body, &reply) |
1869 | + if err != nil { |
1870 | + svc.Log.Errorf("Unmarshalling response body: %v", err) |
1871 | + return nil, fmt.Errorf("unable to unmarshal register response: %v", err) |
1872 | + } |
1873 | + |
1874 | + if !reply.Ok || reply.Token == "" { |
1875 | + svc.Log.Errorf("Unexpected response: %#v", reply) |
1876 | + return nil, BadToken |
1877 | + } |
1878 | + |
1879 | + return []interface{}{reply.Token}, nil |
1880 | } |
1881 | |
1882 | === modified file 'client/service/service_test.go' |
1883 | --- client/service/service_test.go 2014-05-21 10:05:24 +0000 |
1884 | +++ client/service/service_test.go 2014-07-02 13:12:36 +0000 |
1885 | @@ -17,7 +17,10 @@ |
1886 | package service |
1887 | |
1888 | import ( |
1889 | - "errors" |
1890 | + "encoding/json" |
1891 | + "fmt" |
1892 | + "net/http" |
1893 | + "net/http/httptest" |
1894 | "os" |
1895 | "testing" |
1896 | |
1897 | @@ -45,7 +48,7 @@ |
1898 | } |
1899 | |
1900 | func (ss *serviceSuite) TestStart(c *C) { |
1901 | - svc := NewService(ss.bus, ss.log) |
1902 | + svc := NewPushService(ss.bus, ss.log) |
1903 | c.Check(svc.IsRunning(), Equals, false) |
1904 | c.Check(svc.Start(), IsNil) |
1905 | c.Check(svc.IsRunning(), Equals, true) |
1906 | @@ -53,31 +56,31 @@ |
1907 | } |
1908 | |
1909 | func (ss *serviceSuite) TestStartTwice(c *C) { |
1910 | - svc := NewService(ss.bus, ss.log) |
1911 | + svc := NewPushService(ss.bus, ss.log) |
1912 | c.Check(svc.Start(), IsNil) |
1913 | c.Check(svc.Start(), Equals, AlreadyStarted) |
1914 | svc.Stop() |
1915 | } |
1916 | |
1917 | func (ss *serviceSuite) TestStartNoLog(c *C) { |
1918 | - svc := NewService(ss.bus, nil) |
1919 | + svc := NewPushService(ss.bus, nil) |
1920 | c.Check(svc.Start(), Equals, NotConfigured) |
1921 | } |
1922 | |
1923 | func (ss *serviceSuite) TestStartNoBus(c *C) { |
1924 | - svc := NewService(nil, ss.log) |
1925 | + svc := NewPushService(nil, ss.log) |
1926 | c.Check(svc.Start(), Equals, NotConfigured) |
1927 | } |
1928 | |
1929 | func (ss *serviceSuite) TestStartFailsOnBusDialFailure(c *C) { |
1930 | bus := testibus.NewTestingEndpoint(condition.Work(false), nil) |
1931 | - svc := NewService(bus, ss.log) |
1932 | + svc := NewPushService(bus, ss.log) |
1933 | c.Check(svc.Start(), ErrorMatches, `.*(?i)cond said no.*`) |
1934 | svc.Stop() |
1935 | } |
1936 | |
1937 | func (ss *serviceSuite) TestStartGrabsName(c *C) { |
1938 | - svc := NewService(ss.bus, ss.log) |
1939 | + svc := NewPushService(ss.bus, ss.log) |
1940 | c.Assert(svc.Start(), IsNil) |
1941 | callArgs := testibus.GetCallArgs(ss.bus) |
1942 | defer svc.Stop() |
1943 | @@ -86,7 +89,7 @@ |
1944 | } |
1945 | |
1946 | func (ss *serviceSuite) TestStopClosesBus(c *C) { |
1947 | - svc := NewService(ss.bus, ss.log) |
1948 | + svc := NewPushService(ss.bus, ss.log) |
1949 | c.Assert(svc.Start(), IsNil) |
1950 | svc.Stop() |
1951 | callArgs := testibus.GetCallArgs(ss.bus) |
1952 | @@ -96,36 +99,75 @@ |
1953 | |
1954 | // registration tests |
1955 | |
1956 | +func (ss *serviceSuite) TestSetRegURLWorks(c *C) { |
1957 | + svc := NewPushService(ss.bus, ss.log) |
1958 | + c.Check(svc.regURL, Equals, "") |
1959 | + svc.SetRegistrationURL("xyzzy://") |
1960 | + c.Check(svc.regURL, Equals, "xyzzy://") |
1961 | +} |
1962 | + |
1963 | +func (ss *serviceSuite) TestSetAuthGetterWorks(c *C) { |
1964 | + svc := NewPushService(ss.bus, ss.log) |
1965 | + c.Check(svc.authGetter, IsNil) |
1966 | + f := func(string) string { return "" } |
1967 | + svc.SetAuthGetter(f) |
1968 | + c.Check(fmt.Sprintf("%#v", svc.authGetter), Equals, fmt.Sprintf("%#v", f)) |
1969 | +} |
1970 | + |
1971 | +func (ss *serviceSuite) TestGetRegAuthWorks(c *C) { |
1972 | + svc := NewPushService(ss.bus, ss.log) |
1973 | + svc.SetRegistrationURL("xyzzy://") |
1974 | + ch := make(chan string, 1) |
1975 | + f := func(s string) string { ch <- s; return "Auth " + s } |
1976 | + svc.SetAuthGetter(f) |
1977 | + c.Check(svc.getRegistrationAuthorization(), Equals, "Auth xyzzy://") |
1978 | + c.Assert(len(ch), Equals, 1) |
1979 | + c.Check(<-ch, Equals, "xyzzy://") |
1980 | +} |
1981 | + |
1982 | +func (ss *serviceSuite) TestGetRegAuthDoesNotPanic(c *C) { |
1983 | + svc := NewPushService(ss.bus, ss.log) |
1984 | + c.Check(svc.getRegistrationAuthorization(), Equals, "") |
1985 | +} |
1986 | + |
1987 | func (ss *serviceSuite) TestRegistrationFailsIfBadArgs(c *C) { |
1988 | - for i, s := range []struct { |
1989 | - args []interface{} |
1990 | - errt error |
1991 | - }{ |
1992 | - {nil, BadArgCount}, // no args |
1993 | - {[]interface{}{}, BadArgCount}, // still no args |
1994 | - {[]interface{}{42}, BadArgType}, // bad arg type |
1995 | - {[]interface{}{1, 2}, BadArgCount}, // too many args |
1996 | - } { |
1997 | - reg, err := new(Service).register(s.args, nil) |
1998 | - c.Check(reg, IsNil, Commentf("iteration #%d", i)) |
1999 | - c.Check(err, Equals, s.errt, Commentf("iteration #%d", i)) |
2000 | - } |
2001 | + reg, err := new(PushService).register("", []interface{}{1}, nil) |
2002 | + c.Check(reg, IsNil) |
2003 | + c.Check(err, Equals, BadArgCount) |
2004 | } |
2005 | |
2006 | func (ss *serviceSuite) TestRegistrationWorks(c *C) { |
2007 | - reg, err := new(Service).register([]interface{}{"this"}, nil) |
2008 | + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
2009 | + buf := make([]byte, 256) |
2010 | + n, e := r.Body.Read(buf) |
2011 | + c.Assert(e, IsNil) |
2012 | + req := registrationRequest{} |
2013 | + c.Assert(json.Unmarshal(buf[:n], &req), IsNil) |
2014 | + c.Check(req, DeepEquals, registrationRequest{"fake-device-id", "an-app-id"}) |
2015 | + |
2016 | + w.Header().Set("Content-Type", "application/json") |
2017 | + fmt.Fprintln(w, `{"ok":true,"token":"blob-of-bytes"}`) |
2018 | + })) |
2019 | + defer ts.Close() |
2020 | + |
2021 | + svc := NewPushService(ss.bus, ss.log) |
2022 | + svc.SetAuthGetter(func(string) string { return "tok" }) |
2023 | + svc.SetRegistrationURL(ts.URL) |
2024 | + svc.SetDeviceId("fake-device-id") |
2025 | + // this'll check (un)quoting, too |
2026 | + reg, err := svc.register("/an_2dapp_2did", nil, nil) |
2027 | + c.Assert(err, IsNil) |
2028 | c.Assert(reg, HasLen, 1) |
2029 | regs, ok := reg[0].(string) |
2030 | c.Check(ok, Equals, true) |
2031 | - c.Check(regs, Not(Equals), "") |
2032 | - c.Check(err, IsNil) |
2033 | + c.Check(regs, Equals, "blob-of-bytes") |
2034 | } |
2035 | |
2036 | func (ss *serviceSuite) TestRegistrationOverrideWorks(c *C) { |
2037 | os.Setenv("PUSH_REG_stuff", "42") |
2038 | defer os.Setenv("PUSH_REG_stuff", "") |
2039 | |
2040 | - reg, err := new(Service).register([]interface{}{"stuff"}, nil) |
2041 | + reg, err := new(PushService).register("/stuff", nil, nil) |
2042 | c.Assert(reg, HasLen, 1) |
2043 | regs, ok := reg[0].(string) |
2044 | c.Check(ok, Equals, true) |
2045 | @@ -133,115 +175,103 @@ |
2046 | c.Check(err, IsNil) |
2047 | } |
2048 | |
2049 | -// |
2050 | -// Injection tests |
2051 | - |
2052 | -func (ss *serviceSuite) TestInjectWorks(c *C) { |
2053 | - svc := NewService(ss.bus, ss.log) |
2054 | - rvs, err := svc.inject([]interface{}{"hello", "world"}, nil) |
2055 | - c.Assert(err, IsNil) |
2056 | - c.Check(rvs, IsNil) |
2057 | - rvs, err = svc.inject([]interface{}{"hello", "there"}, nil) |
2058 | - c.Assert(err, IsNil) |
2059 | - c.Check(rvs, IsNil) |
2060 | - c.Assert(svc.mbox, HasLen, 1) |
2061 | - c.Assert(svc.mbox["hello"], HasLen, 2) |
2062 | - c.Check(svc.mbox["hello"][0], Equals, "world") |
2063 | - c.Check(svc.mbox["hello"][1], Equals, "there") |
2064 | - |
2065 | - // and check it fired the right signal (twice) |
2066 | - callArgs := testibus.GetCallArgs(ss.bus) |
2067 | - c.Assert(callArgs, HasLen, 2) |
2068 | - c.Check(callArgs[0].Member, Equals, "::Signal") |
2069 | - c.Check(callArgs[0].Args, DeepEquals, []interface{}{"Notification", []interface{}{"hello"}}) |
2070 | - c.Check(callArgs[1], DeepEquals, callArgs[0]) |
2071 | -} |
2072 | - |
2073 | -func (ss *serviceSuite) TestInjectFailsIfInjectFails(c *C) { |
2074 | - bus := testibus.NewTestingEndpoint(condition.Work(true), |
2075 | - condition.Work(false)) |
2076 | - svc := NewService(bus, ss.log) |
2077 | - svc.SetMessageHandler(func([]byte) error { return errors.New("fail") }) |
2078 | - _, err := svc.inject([]interface{}{"hello", "xyzzy"}, nil) |
2079 | - c.Check(err, NotNil) |
2080 | -} |
2081 | - |
2082 | -func (ss *serviceSuite) TestInjectFailsIfBadArgs(c *C) { |
2083 | - for i, s := range []struct { |
2084 | - args []interface{} |
2085 | - errt error |
2086 | - }{ |
2087 | - {nil, BadArgCount}, |
2088 | - {[]interface{}{}, BadArgCount}, |
2089 | - {[]interface{}{1}, BadArgCount}, |
2090 | - {[]interface{}{1, 2}, BadArgType}, |
2091 | - {[]interface{}{"1", 2}, BadArgType}, |
2092 | - {[]interface{}{1, "2"}, BadArgType}, |
2093 | - {[]interface{}{1, 2, 3}, BadArgCount}, |
2094 | - } { |
2095 | - reg, err := new(Service).inject(s.args, nil) |
2096 | - c.Check(reg, IsNil, Commentf("iteration #%d", i)) |
2097 | - c.Check(err, Equals, s.errt, Commentf("iteration #%d", i)) |
2098 | - } |
2099 | -} |
2100 | - |
2101 | -// |
2102 | -// Notifications tests |
2103 | -func (ss *serviceSuite) TestNotificationsWorks(c *C) { |
2104 | - svc := NewService(ss.bus, ss.log) |
2105 | - nots, err := svc.notifications([]interface{}{"hello"}, nil) |
2106 | - c.Assert(err, IsNil) |
2107 | - c.Assert(nots, NotNil) |
2108 | - c.Assert(nots, HasLen, 1) |
2109 | - c.Check(nots[0], HasLen, 0) |
2110 | - if svc.mbox == nil { |
2111 | - svc.mbox = make(map[string][]string) |
2112 | - } |
2113 | - svc.mbox["hello"] = append(svc.mbox["hello"], "this", "thing") |
2114 | - nots, err = svc.notifications([]interface{}{"hello"}, nil) |
2115 | - c.Assert(err, IsNil) |
2116 | - c.Assert(nots, NotNil) |
2117 | - c.Assert(nots, HasLen, 1) |
2118 | - c.Check(nots[0], DeepEquals, []string{"this", "thing"}) |
2119 | -} |
2120 | - |
2121 | -func (ss *serviceSuite) TestNotificationsFailsIfBadArgs(c *C) { |
2122 | - for i, s := range []struct { |
2123 | - args []interface{} |
2124 | - errt error |
2125 | - }{ |
2126 | - {nil, BadArgCount}, // no args |
2127 | - {[]interface{}{}, BadArgCount}, // still no args |
2128 | - {[]interface{}{42}, BadArgType}, // bad arg type |
2129 | - {[]interface{}{1, 2}, BadArgCount}, // too many args |
2130 | - } { |
2131 | - reg, err := new(Service).notifications(s.args, nil) |
2132 | - c.Check(reg, IsNil, Commentf("iteration #%d", i)) |
2133 | - c.Check(err, Equals, s.errt, Commentf("iteration #%d", i)) |
2134 | - } |
2135 | -} |
2136 | - |
2137 | -func (ss *serviceSuite) TestMessageHandler(c *C) { |
2138 | - svc := new(Service) |
2139 | - c.Assert(svc.msgHandler, IsNil) |
2140 | - var ext = []byte{} |
2141 | - e := errors.New("Hello") |
2142 | - f := func(s []byte) error { ext = s; return e } |
2143 | - c.Check(svc.GetMessageHandler(), IsNil) |
2144 | - svc.SetMessageHandler(f) |
2145 | - c.Check(svc.GetMessageHandler(), NotNil) |
2146 | - c.Check(svc.msgHandler([]byte("37")), Equals, e) |
2147 | - c.Check(ext, DeepEquals, []byte("37")) |
2148 | -} |
2149 | - |
2150 | -func (ss *serviceSuite) TestInjectCallsMessageHandler(c *C) { |
2151 | - var ext = []byte{} |
2152 | - svc := NewService(ss.bus, ss.log) |
2153 | - f := func(s []byte) error { ext = s; return nil } |
2154 | - svc.SetMessageHandler(f) |
2155 | - c.Check(svc.Inject("stuff", "{}"), IsNil) |
2156 | - c.Check(ext, DeepEquals, []byte("{}")) |
2157 | - err := errors.New("ouch") |
2158 | - svc.SetMessageHandler(func([]byte) error { return err }) |
2159 | - c.Check(svc.Inject("stuff", "{}"), Equals, err) |
2160 | +func (ss *serviceSuite) TestRegistrationFailsOnBadReqURL(c *C) { |
2161 | + svc := NewPushService(ss.bus, ss.log) |
2162 | + svc.SetRegistrationURL("%gh") |
2163 | + reg, err := svc.register("thing", nil, nil) |
2164 | + c.Check(reg, IsNil) |
2165 | + c.Check(err, ErrorMatches, "unable to build register request: .*") |
2166 | +} |
2167 | + |
2168 | +func (ss *serviceSuite) TestRegistrationFailsOnBadAuth(c *C) { |
2169 | + svc := NewPushService(ss.bus, ss.log) |
2170 | + // ... no auth added |
2171 | + reg, err := svc.register("thing", nil, nil) |
2172 | + c.Check(reg, IsNil) |
2173 | + c.Check(err, Equals, BadAuth) |
2174 | +} |
2175 | + |
2176 | +func (ss *serviceSuite) TestRegistrationFailsOnNoServer(c *C) { |
2177 | + svc := NewPushService(ss.bus, ss.log) |
2178 | + svc.SetRegistrationURL("xyzzy://") |
2179 | + svc.SetAuthGetter(func(string) string { return "tok" }) |
2180 | + reg, err := svc.register("thing", nil, nil) |
2181 | + c.Check(reg, IsNil) |
2182 | + c.Check(err, ErrorMatches, "unable to request registration: .*") |
2183 | +} |
2184 | + |
2185 | +func (ss *serviceSuite) TestRegistrationFailsOn40x(c *C) { |
2186 | + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
2187 | + http.Error(w, "I'm a teapot", 418) |
2188 | + })) |
2189 | + defer ts.Close() |
2190 | + |
2191 | + svc := NewPushService(ss.bus, ss.log) |
2192 | + svc.SetAuthGetter(func(string) string { return "tok" }) |
2193 | + svc.SetRegistrationURL(ts.URL) |
2194 | + reg, err := svc.register("/thing", nil, nil) |
2195 | + c.Check(err, Equals, BadRequest) |
2196 | + c.Check(reg, IsNil) |
2197 | +} |
2198 | + |
2199 | +func (ss *serviceSuite) TestRegistrationFailsOn50x(c *C) { |
2200 | + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
2201 | + http.Error(w, "Not implemented", 501) |
2202 | + })) |
2203 | + defer ts.Close() |
2204 | + |
2205 | + svc := NewPushService(ss.bus, ss.log) |
2206 | + svc.SetAuthGetter(func(string) string { return "tok" }) |
2207 | + svc.SetRegistrationURL(ts.URL) |
2208 | + reg, err := svc.register("/thing", nil, nil) |
2209 | + c.Check(err, Equals, BadServer) |
2210 | + c.Check(reg, IsNil) |
2211 | +} |
2212 | + |
2213 | +func (ss *serviceSuite) TestRegistrationFailsOnBadJSON(c *C) { |
2214 | + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
2215 | + buf := make([]byte, 256) |
2216 | + n, e := r.Body.Read(buf) |
2217 | + c.Assert(e, IsNil) |
2218 | + req := registrationRequest{} |
2219 | + c.Assert(json.Unmarshal(buf[:n], &req), IsNil) |
2220 | + c.Check(req, DeepEquals, registrationRequest{"fake-device-id", "an-app-id"}) |
2221 | + |
2222 | + w.Header().Set("Content-Type", "application/json") |
2223 | + fmt.Fprintln(w, `{`) |
2224 | + })) |
2225 | + defer ts.Close() |
2226 | + |
2227 | + svc := NewPushService(ss.bus, ss.log) |
2228 | + svc.SetAuthGetter(func(string) string { return "tok" }) |
2229 | + svc.SetRegistrationURL(ts.URL) |
2230 | + svc.SetDeviceId("fake-device-id") |
2231 | + // this'll check (un)quoting, too |
2232 | + reg, err := svc.register("/an_2dapp_2did", nil, nil) |
2233 | + c.Check(reg, IsNil) |
2234 | + c.Check(err, ErrorMatches, "unable to unmarshal register response: .*") |
2235 | +} |
2236 | + |
2237 | +func (ss *serviceSuite) TestRegistrationFailsOnBadJSONDocument(c *C) { |
2238 | + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
2239 | + buf := make([]byte, 256) |
2240 | + n, e := r.Body.Read(buf) |
2241 | + c.Assert(e, IsNil) |
2242 | + req := registrationRequest{} |
2243 | + c.Assert(json.Unmarshal(buf[:n], &req), IsNil) |
2244 | + c.Check(req, DeepEquals, registrationRequest{"fake-device-id", "an-app-id"}) |
2245 | + |
2246 | + w.Header().Set("Content-Type", "application/json") |
2247 | + fmt.Fprintln(w, `{"bananas": "very yes"}`) |
2248 | + })) |
2249 | + defer ts.Close() |
2250 | + |
2251 | + svc := NewPushService(ss.bus, ss.log) |
2252 | + svc.SetAuthGetter(func(string) string { return "tok" }) |
2253 | + svc.SetRegistrationURL(ts.URL) |
2254 | + svc.SetDeviceId("fake-device-id") |
2255 | + // this'll check (un)quoting, too |
2256 | + reg, err := svc.register("/an_2dapp_2did", nil, nil) |
2257 | + c.Check(reg, IsNil) |
2258 | + c.Check(err, Equals, BadToken) |
2259 | } |
2260 | |
2261 | === modified file 'client/session/session.go' |
2262 | --- client/session/session.go 2014-05-23 05:59:38 +0000 |
2263 | +++ client/session/session.go 2014-07-02 13:12:36 +0000 |
2264 | @@ -26,7 +26,6 @@ |
2265 | "fmt" |
2266 | "math/rand" |
2267 | "net" |
2268 | - "os/exec" |
2269 | "strings" |
2270 | "sync" |
2271 | "sync/atomic" |
2272 | @@ -87,7 +86,8 @@ |
2273 | ExpectAllRepairedTime time.Duration |
2274 | PEM []byte |
2275 | Info map[string]interface{} |
2276 | - AuthHelper []string |
2277 | + AuthGetter func(string) string |
2278 | + AuthURL string |
2279 | } |
2280 | |
2281 | // ClientSession holds a client<->server session and its configuration. |
2282 | @@ -244,21 +244,10 @@ |
2283 | // addAuthorization gets the authorization blob to send to the server |
2284 | // and adds it to the session. |
2285 | func (sess *ClientSession) addAuthorization() error { |
2286 | - sess.Log.Debugf("adding authorization") |
2287 | - // using a helper, for now at least |
2288 | - if len(sess.AuthHelper) == 0 { |
2289 | - // do nothing if helper is unset or empty |
2290 | - return nil |
2291 | - } |
2292 | - |
2293 | - auth, err := exec.Command(sess.AuthHelper[0], sess.AuthHelper[1:]...).Output() |
2294 | - if err != nil { |
2295 | - // For now we just log the error, as we don't want to block unauthorized users |
2296 | - sess.Log.Errorf("unable to get the authorization token from the account: %v", err) |
2297 | - } else { |
2298 | - sess.auth = strings.TrimSpace(string(auth)) |
2299 | - } |
2300 | - |
2301 | + if sess.AuthGetter != nil { |
2302 | + sess.Log.Debugf("adding authorization") |
2303 | + sess.auth = sess.AuthGetter(sess.AuthURL) |
2304 | + } |
2305 | return nil |
2306 | } |
2307 | |
2308 | |
2309 | === modified file 'client/session/session_test.go' |
2310 | --- client/session/session_test.go 2014-05-23 05:59:38 +0000 |
2311 | +++ client/session/session_test.go 2014-07-02 13:12:36 +0000 |
2312 | @@ -353,34 +353,21 @@ |
2313 | ****************************************************************/ |
2314 | |
2315 | func (cs *clientSessionSuite) TestAddAuthorizationAddsAuthorization(c *C) { |
2316 | - sess := &ClientSession{Log: cs.log} |
2317 | - sess.AuthHelper = []string{"echo", "some auth"} |
2318 | - c.Assert(sess.auth, Equals, "") |
2319 | - err := sess.addAuthorization() |
2320 | - c.Assert(err, IsNil) |
2321 | - c.Check(sess.auth, Equals, "some auth") |
2322 | -} |
2323 | - |
2324 | -func (cs *clientSessionSuite) TestAddAuthorizationIgnoresErrors(c *C) { |
2325 | - sess := &ClientSession{Log: cs.log} |
2326 | - sess.AuthHelper = []string{"sh", "-c", "echo hello; false"} |
2327 | - |
2328 | - c.Assert(sess.auth, Equals, "") |
2329 | - err := sess.addAuthorization() |
2330 | - c.Assert(err, IsNil) |
2331 | - c.Check(sess.auth, Equals, "") |
2332 | -} |
2333 | - |
2334 | -func (cs *clientSessionSuite) TestAddAuthorizationSkipsIfUnsetOrNil(c *C) { |
2335 | - sess := &ClientSession{Log: cs.log} |
2336 | - sess.AuthHelper = nil |
2337 | - c.Assert(sess.auth, Equals, "") |
2338 | - err := sess.addAuthorization() |
2339 | - c.Assert(err, IsNil) |
2340 | - c.Check(sess.auth, Equals, "") |
2341 | - |
2342 | - sess.AuthHelper = []string{} |
2343 | - err = sess.addAuthorization() |
2344 | + url := "xyzzy://" |
2345 | + sess := &ClientSession{Log: cs.log} |
2346 | + sess.AuthGetter = func(url string) string { return url + " auth'ed" } |
2347 | + sess.AuthURL = url |
2348 | + c.Assert(sess.auth, Equals, "") |
2349 | + err := sess.addAuthorization() |
2350 | + c.Assert(err, IsNil) |
2351 | + c.Check(sess.auth, Equals, "xyzzy:// auth'ed") |
2352 | +} |
2353 | + |
2354 | +func (cs *clientSessionSuite) TestAddAuthorizationSkipsIfUnset(c *C) { |
2355 | + sess := &ClientSession{Log: cs.log} |
2356 | + sess.AuthGetter = nil |
2357 | + c.Assert(sess.auth, Equals, "") |
2358 | + err := sess.addAuthorization() |
2359 | c.Assert(err, IsNil) |
2360 | c.Check(sess.auth, Equals, "") |
2361 | } |
2362 | |
2363 | === modified file 'debian/changelog' |
2364 | --- debian/changelog 2014-06-05 09:42:22 +0000 |
2365 | +++ debian/changelog 2014-07-02 13:12:36 +0000 |
2366 | @@ -1,3 +1,37 @@ |
2367 | +ubuntu-push (0.42ubuntu1) UNRELEASED; urgency=medium |
2368 | + |
2369 | + [ Samuele Pedroni ] |
2370 | + * Support registering tokens and sending notifications with a token |
2371 | + * Register script and scripts unicast support |
2372 | + * Update http13client from the actual go1.3 release |
2373 | + * Avoid late pings in the face of nop exchanges |
2374 | + * murmur3 upstream change of seed to 0 |
2375 | + |
2376 | + [ Roberto Alsina ] |
2377 | + * Make signing-helper generate a HTTP header instead of a querystring, |
2378 | + and take a URL to sign. |
2379 | + * Wrap libmessaging-menu to allow for persistent notifications. |
2380 | + * Wrap ubuntu-app-launch start_helper / stop_helper functions. |
2381 | + |
2382 | + [ John R. Lenton ] |
2383 | + * Switch dbus api to retrieve app name from dbus path. |
2384 | + * Move signing bits up from session to client, for reuse by service. |
2385 | + * Change AuthHelper to be a string; auth helper should now expect a |
2386 | + parameter (the url to sign). Added SessionURL to config. |
2387 | + * Adapt our whoopsie wrapper to whoopsie's now more correct behavior wrt |
2388 | + failing to get a mac address. |
2389 | + * Add registration_url to config; hook up auth bits and reg url to |
2390 | + client & service. |
2391 | + * Do an HTTP POST to registration_url on register. |
2392 | + * Fix debian/rules so packaging-time tests pass (ugh) |
2393 | + * Refactoring notification providers. |
2394 | + * Get the small messaging-menu wrapper working (thanks larsu & dednick) |
2395 | + |
2396 | + [ Guillermo Gonzalez ] |
2397 | + * Split DBus service into PushService and PostalService |
2398 | + |
2399 | + -- John R. Lenton <john.lenton@canonical.com> Wed, 02 Jul 2014 13:49:01 +0100 |
2400 | + |
2401 | ubuntu-push (0.3+14.10.20140605-0ubuntu1) utopic; urgency=medium |
2402 | |
2403 | [ John Lenton ] |
2404 | |
2405 | === modified file 'debian/config.json' |
2406 | --- debian/config.json 2014-06-04 12:18:42 +0000 |
2407 | +++ debian/config.json 2014-07-02 13:12:36 +0000 |
2408 | @@ -1,5 +1,7 @@ |
2409 | { |
2410 | - "auth_helper": [], |
2411 | + "auth_helper": "/usr/lib/ubuntu-push-client/signing-helper", |
2412 | + "session_url": "https://push.ubuntu.com/", |
2413 | + "registration_url": "https://push.ubuntu.com/register", |
2414 | "connect_timeout": "20s", |
2415 | "exchange_timeout": "30s", |
2416 | "hosts_cache_expiry": "12h", |
2417 | |
2418 | === modified file 'debian/control' |
2419 | --- debian/control 2014-06-02 10:01:13 +0000 |
2420 | +++ debian/control 2014-07-02 13:12:36 +0000 |
2421 | @@ -14,7 +14,9 @@ |
2422 | golang-uuid-dev, |
2423 | libgcrypt11-dev, |
2424 | libglib2.0-dev (>= 2.31.6), |
2425 | + libmessaging-menu-dev, |
2426 | libwhoopsie-dev, |
2427 | + libubuntu-app-launch2-dev, |
2428 | libubuntuoneauth-2.0-dev, |
2429 | libdbus-1-dev, |
2430 | libnih-dbus-dev, |
2431 | |
2432 | === modified file 'debian/rules' |
2433 | --- debian/rules 2014-05-02 12:42:27 +0000 |
2434 | +++ debian/rules 2014-07-02 13:12:36 +0000 |
2435 | @@ -5,10 +5,15 @@ |
2436 | export UBUNTU_PUSH_TEST_RESOURCES_ROOT := $(CURDIR) |
2437 | |
2438 | override_dh_auto_build: |
2439 | - cd $$( find ./ -type d -regex '\./[^/]*/src/launchpad.net' -printf "%h\n" | head -n1) |
2440 | dh_auto_build --buildsystem=golang |
2441 | (cd signing-helper && cmake . && make) |
2442 | |
2443 | +# overriding dh_auto_test because the http13client tests don't all pass on go < 1.3 |
2444 | +# (should go away once we ship go 1.3) |
2445 | +override_dh_auto_test: |
2446 | + cd $$( find ./ -type d -regex '\./[^/]*/src/launchpad.net' -printf "%h\n" | head -n1) && \ |
2447 | + env GOPATH=$$(cd ..; pwd) go test $$(env GOPATH=$$(cd ..; pwd) go list $(DH_GOPKG)/... | grep -v acceptance | grep -v http13client ) |
2448 | + |
2449 | override_dh_install: |
2450 | dh_install -Xusr/bin/cmd -Xusr/bin/dev --fail-missing |
2451 | |
2452 | |
2453 | === modified file 'external/README' |
2454 | --- external/README 2014-03-24 15:37:36 +0000 |
2455 | +++ external/README 2014-07-02 13:12:36 +0000 |
2456 | @@ -1,3 +1,3 @@ |
2457 | Directly included vendorized small packages. |
2458 | |
2459 | -* murmor3 comes from import at lp:~ubuntu-push-hackers/ubuntu-push/murmur at revno 10 |
2460 | +* murmur3 comes from import at lp:~ubuntu-push-hackers/ubuntu-push/murmur at revno 12 |
2461 | |
2462 | === modified file 'external/murmur3/murmur128.go' |
2463 | --- external/murmur3/murmur128.go 2014-03-24 15:31:42 +0000 |
2464 | +++ external/murmur3/murmur128.go 2014-07-02 13:12:36 +0000 |
2465 | @@ -40,7 +40,7 @@ |
2466 | |
2467 | func (d *digest128) Size() int { return 16 } |
2468 | |
2469 | -func (d *digest128) reset() { d.h1, d.h2 = 1, 1 } |
2470 | +func (d *digest128) reset() { d.h1, d.h2 = 0, 0 } |
2471 | |
2472 | func (d *digest128) Sum(b []byte) []byte { |
2473 | h1, h2 := d.h1, d.h2 |
2474 | @@ -182,7 +182,7 @@ |
2475 | // hasher.Write(data) |
2476 | // return hasher.Sum128() |
2477 | func Sum128(data []byte) (h1 uint64, h2 uint64) { |
2478 | - d := &digest128{h1: 1, h2: 1} |
2479 | + d := &digest128{h1: 0, h2: 0} |
2480 | d.tail = d.bmix(data) |
2481 | d.clen = len(data) |
2482 | return d.Sum128() |
2483 | |
2484 | === modified file 'external/murmur3/murmur32.go' |
2485 | --- external/murmur3/murmur32.go 2014-03-24 15:31:42 +0000 |
2486 | +++ external/murmur3/murmur32.go 2014-07-02 13:12:36 +0000 |
2487 | @@ -33,7 +33,7 @@ |
2488 | |
2489 | func (d *digest32) Size() int { return 4 } |
2490 | |
2491 | -func (d *digest32) reset() { d.h1 = 1 } |
2492 | +func (d *digest32) reset() { d.h1 = 0 } |
2493 | |
2494 | func (d *digest32) Sum(b []byte) []byte { |
2495 | h := d.h1 |
2496 | @@ -104,7 +104,7 @@ |
2497 | // return hasher.Sum32() |
2498 | func Sum32(data []byte) uint32 { |
2499 | |
2500 | - var h1 uint32 = 1 |
2501 | + var h1 uint32 = 0 |
2502 | |
2503 | nblocks := len(data) / 4 |
2504 | var p uintptr |
2505 | |
2506 | === modified file 'external/murmur3/murmur64.go' |
2507 | --- external/murmur3/murmur64.go 2014-03-24 15:31:42 +0000 |
2508 | +++ external/murmur3/murmur64.go 2014-07-02 13:12:36 +0000 |
2509 | @@ -37,7 +37,7 @@ |
2510 | // hasher.Write(data) |
2511 | // return hasher.Sum64() |
2512 | func Sum64(data []byte) uint64 { |
2513 | - d := &digest128{h1: 1, h2: 1} |
2514 | + d := &digest128{h1: 0, h2: 0} |
2515 | d.tail = d.bmix(data) |
2516 | d.clen = len(data) |
2517 | h1, _ := d.Sum128() |
2518 | |
2519 | === modified file 'external/murmur3/murmur_test.go' |
2520 | --- external/murmur3/murmur_test.go 2014-03-24 15:31:42 +0000 |
2521 | +++ external/murmur3/murmur_test.go 2014-07-02 13:12:36 +0000 |
2522 | @@ -11,11 +11,11 @@ |
2523 | h64_2 uint64 |
2524 | s string |
2525 | }{ |
2526 | - {0x514e28b7, 0x4610abe56eff5cb5, 0x51622daa78f83583, ""}, |
2527 | - {0xbb4abcad, 0xa78ddff5adae8d10, 0x128900ef20900135, "hello"}, |
2528 | - {0x6f5cb2e9, 0x8b95f808840725c6, 0x1597ed5422bd493b, "hello, world"}, |
2529 | - {0xf50e1f30, 0x2a929de9c8f97b2f, 0x56a41d99af43a2db, "19 Jan 2038 at 3:14:07 AM"}, |
2530 | - {0x846f6a36, 0xfb3325171f9744da, 0xaaf8b92a5f722952, "The quick brown fox jumps over the lazy dog."}, |
2531 | + {0x00000000, 0x0000000000000000, 0x0000000000000000, ""}, |
2532 | + {0x248bfa47, 0xcbd8a7b341bd9b02, 0x5b1e906a48ae1d19, "hello"}, |
2533 | + {0x149bbb7f, 0x342fac623a5ebc8e, 0x4cdcbc079642414d, "hello, world"}, |
2534 | + {0xe31e8a70, 0xb89e5988b737affc, 0x664fc2950231b2cb, "19 Jan 2038 at 3:14:07 AM"}, |
2535 | + {0xd5c48bfc, 0xcd99481f9ee902c9, 0x695da1a38987b6e7, "The quick brown fox jumps over the lazy dog."}, |
2536 | } |
2537 | |
2538 | func TestRef(t *testing.T) { |
2539 | @@ -37,6 +37,10 @@ |
2540 | t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h64_1) |
2541 | } |
2542 | |
2543 | + if v := Sum64([]byte(elem.s)); v != elem.h64_1 { |
2544 | + t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h64_1) |
2545 | + } |
2546 | + |
2547 | var h128 Hash128 = New128() |
2548 | h128.Write([]byte(elem.s)) |
2549 | if v1, v2 := h128.Sum128(); v1 != elem.h64_1 || v2 != elem.h64_2 { |
2550 | |
2551 | === modified file 'http13client/Makefile' |
2552 | --- http13client/Makefile 2014-03-20 12:15:36 +0000 |
2553 | +++ http13client/Makefile 2014-07-02 13:12:36 +0000 |
2554 | @@ -20,7 +20,8 @@ |
2555 | |
2556 | prune: |
2557 | rm -rf example_test.go filetransport*.go fs*.go race.go range_test.go \ |
2558 | - sniff*.go httptest httputil testdata triv.go jar.go status.go |
2559 | + sniff*.go httptest httputil testdata triv.go jar.go status.go \ |
2560 | + cookie_test.go |
2561 | sed -i -e 's+"launchpad.net/ubuntu-push/http13client/+"net/http/+' *.go |
2562 | |
2563 | fix: |
2564 | |
2565 | === modified file 'http13client/_patches/empty_server.patch' |
2566 | --- http13client/_patches/empty_server.patch 2014-03-19 23:13:58 +0000 |
2567 | +++ http13client/_patches/empty_server.patch 2014-07-02 13:12:36 +0000 |
2568 | @@ -1,6 +1,6 @@ |
2569 | === modified file 'http13client/serve_test.go' |
2570 | ---- http13client/serve_test.go 2014-03-19 21:38:56 +0000 |
2571 | -+++ http13client/serve_test.go 2014-03-19 22:27:37 +0000 |
2572 | +--- http13client/serve_test.go 2014-06-20 11:00:47 +0000 |
2573 | ++++ http13client/serve_test.go 2014-06-20 12:00:22 +0000 |
2574 | @@ -2,60 +2,15 @@ |
2575 | // Use of this source code is governed by a BSD-style |
2576 | // license that can be found in the LICENSE file. |
2577 | @@ -62,7 +62,7 @@ |
2578 | |
2579 | func (a dummyAddr) Network() string { |
2580 | return string(a) |
2581 | -@@ -93,1289 +48,6 @@ |
2582 | +@@ -93,1325 +48,6 @@ |
2583 | return nil |
2584 | } |
2585 | |
2586 | @@ -906,31 +906,50 @@ |
2587 | -} |
2588 | - |
2589 | -type serverExpectTest struct { |
2590 | -- contentLength int // of request body |
2591 | +- contentLength int // of request body |
2592 | +- chunked bool |
2593 | - expectation string // e.g. "100-continue" |
2594 | - readBody bool // whether handler should read the body (if false, sends StatusUnauthorized) |
2595 | - expectedResponse string // expected substring in first line of http response |
2596 | -} |
2597 | - |
2598 | +-func expectTest(contentLength int, expectation string, readBody bool, expectedResponse string) serverExpectTest { |
2599 | +- return serverExpectTest{ |
2600 | +- contentLength: contentLength, |
2601 | +- expectation: expectation, |
2602 | +- readBody: readBody, |
2603 | +- expectedResponse: expectedResponse, |
2604 | +- } |
2605 | +-} |
2606 | +- |
2607 | -var serverExpectTests = []serverExpectTest{ |
2608 | - // Normal 100-continues, case-insensitive. |
2609 | -- {100, "100-continue", true, "100 Continue"}, |
2610 | -- {100, "100-cOntInUE", true, "100 Continue"}, |
2611 | +- expectTest(100, "100-continue", true, "100 Continue"), |
2612 | +- expectTest(100, "100-cOntInUE", true, "100 Continue"), |
2613 | - |
2614 | - // No 100-continue. |
2615 | -- {100, "", true, "200 OK"}, |
2616 | +- expectTest(100, "", true, "200 OK"), |
2617 | - |
2618 | - // 100-continue but requesting client to deny us, |
2619 | - // so it never reads the body. |
2620 | -- {100, "100-continue", false, "401 Unauthorized"}, |
2621 | +- expectTest(100, "100-continue", false, "401 Unauthorized"), |
2622 | - // Likewise without 100-continue: |
2623 | -- {100, "", false, "401 Unauthorized"}, |
2624 | +- expectTest(100, "", false, "401 Unauthorized"), |
2625 | - |
2626 | - // Non-standard expectations are failures |
2627 | -- {0, "a-pony", false, "417 Expectation Failed"}, |
2628 | +- expectTest(0, "a-pony", false, "417 Expectation Failed"), |
2629 | - |
2630 | -- // Expect-100 requested but no body |
2631 | -- {0, "100-continue", true, "400 Bad Request"}, |
2632 | +- // Expect-100 requested but no body (is apparently okay: Issue 7625) |
2633 | +- expectTest(0, "100-continue", true, "200 OK"), |
2634 | +- // Expect-100 requested but handler doesn't read the body |
2635 | +- expectTest(0, "100-continue", false, "401 Unauthorized"), |
2636 | +- // Expect-100 continue with no body, but a chunked body. |
2637 | +- { |
2638 | +- expectation: "100-continue", |
2639 | +- readBody: true, |
2640 | +- chunked: true, |
2641 | +- expectedResponse: "100 Continue", |
2642 | +- }, |
2643 | -} |
2644 | - |
2645 | -// Tests that the server responds to the "Expect" request header |
2646 | @@ -959,21 +978,38 @@ |
2647 | - |
2648 | - // Only send the body immediately if we're acting like an HTTP client |
2649 | - // that doesn't send 100-continue expectations. |
2650 | -- writeBody := test.contentLength > 0 && strings.ToLower(test.expectation) != "100-continue" |
2651 | +- writeBody := test.contentLength != 0 && strings.ToLower(test.expectation) != "100-continue" |
2652 | - |
2653 | - go func() { |
2654 | +- contentLen := fmt.Sprintf("Content-Length: %d", test.contentLength) |
2655 | +- if test.chunked { |
2656 | +- contentLen = "Transfer-Encoding: chunked" |
2657 | +- } |
2658 | - _, err := fmt.Fprintf(conn, "POST /?readbody=%v HTTP/1.1\r\n"+ |
2659 | - "Connection: close\r\n"+ |
2660 | -- "Content-Length: %d\r\n"+ |
2661 | +- "%s\r\n"+ |
2662 | - "Expect: %s\r\nHost: foo\r\n\r\n", |
2663 | -- test.readBody, test.contentLength, test.expectation) |
2664 | +- test.readBody, contentLen, test.expectation) |
2665 | - if err != nil { |
2666 | - t.Errorf("On test %#v, error writing request headers: %v", test, err) |
2667 | - return |
2668 | - } |
2669 | - if writeBody { |
2670 | +- var targ io.WriteCloser = struct { |
2671 | +- io.Writer |
2672 | +- io.Closer |
2673 | +- }{ |
2674 | +- conn, |
2675 | +- ioutil.NopCloser(nil), |
2676 | +- } |
2677 | +- if test.chunked { |
2678 | +- targ = httputil.NewChunkedWriter(conn) |
2679 | +- } |
2680 | - body := strings.Repeat("A", test.contentLength) |
2681 | -- _, err = fmt.Fprint(conn, body) |
2682 | +- _, err = fmt.Fprint(targ, body) |
2683 | +- if err == nil { |
2684 | +- err = targ.Close() |
2685 | +- } |
2686 | - if err != nil { |
2687 | - if !test.readBody { |
2688 | - // Server likely already hung up on us. |
2689 | @@ -1352,7 +1388,7 @@ |
2690 | type neverEnding byte |
2691 | |
2692 | func (b neverEnding) Read(p []byte) (n int, err error) { |
2693 | -@@ -1384,1344 +56,3 @@ |
2694 | +@@ -1420,1392 +56,3 @@ |
2695 | } |
2696 | return len(p), nil |
2697 | } |
2698 | @@ -2080,7 +2116,7 @@ |
2699 | - got := ht.rawResponse(req) |
2700 | - wantStatus := fmt.Sprintf("%d %s", code, StatusText(code)) |
2701 | - if !strings.Contains(got, wantStatus) { |
2702 | -- t.Errorf("Code %d: Wanted %q Modified for %q: %s", code, req, got) |
2703 | +- t.Errorf("Code %d: Wanted %q Modified for %q: %s", code, wantStatus, req, got) |
2704 | - } else if strings.Contains(got, "Content-Length") { |
2705 | - t.Errorf("Code %d: Got a Content-Length from %q: %s", code, req, got) |
2706 | - } else if strings.Contains(got, "stuff") { |
2707 | @@ -2090,6 +2126,21 @@ |
2708 | - } |
2709 | -} |
2710 | - |
2711 | +-func TestContentTypeOkayOn204(t *testing.T) { |
2712 | +- ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) { |
2713 | +- w.Header().Set("Content-Length", "123") // suppressed |
2714 | +- w.Header().Set("Content-Type", "foo/bar") |
2715 | +- w.WriteHeader(204) |
2716 | +- })) |
2717 | +- got := ht.rawResponse("GET / HTTP/1.1") |
2718 | +- if !strings.Contains(got, "Content-Type: foo/bar") { |
2719 | +- t.Errorf("Response = %q; want Content-Type: foo/bar", got) |
2720 | +- } |
2721 | +- if strings.Contains(got, "Content-Length: 123") { |
2722 | +- t.Errorf("Response = %q; don't want a Content-Length", got) |
2723 | +- } |
2724 | +-} |
2725 | +- |
2726 | -// Issue 6995 |
2727 | -// A server Handler can receive a Request, and then turn around and |
2728 | -// give a copy of that Request.Body out to the Transport (e.g. any |
2729 | @@ -2261,7 +2312,7 @@ |
2730 | - ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0) |
2731 | - ts.Config.ConnState = func(c net.Conn, state ConnState) { |
2732 | - if c == nil { |
2733 | -- t.Error("nil conn seen in state %s", state) |
2734 | +- t.Errorf("nil conn seen in state %s", state) |
2735 | - return |
2736 | - } |
2737 | - mu.Lock() |
2738 | @@ -2397,6 +2448,39 @@ |
2739 | - } |
2740 | -} |
2741 | - |
2742 | +-// golang.org/issue/7856 |
2743 | +-func TestServerEmptyBodyRace(t *testing.T) { |
2744 | +- defer afterTest(t) |
2745 | +- var n int32 |
2746 | +- ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { |
2747 | +- atomic.AddInt32(&n, 1) |
2748 | +- })) |
2749 | +- defer ts.Close() |
2750 | +- var wg sync.WaitGroup |
2751 | +- const reqs = 20 |
2752 | +- for i := 0; i < reqs; i++ { |
2753 | +- wg.Add(1) |
2754 | +- go func() { |
2755 | +- defer wg.Done() |
2756 | +- res, err := Get(ts.URL) |
2757 | +- if err != nil { |
2758 | +- t.Error(err) |
2759 | +- return |
2760 | +- } |
2761 | +- defer res.Body.Close() |
2762 | +- _, err = io.Copy(ioutil.Discard, res.Body) |
2763 | +- if err != nil { |
2764 | +- t.Error(err) |
2765 | +- return |
2766 | +- } |
2767 | +- }() |
2768 | +- } |
2769 | +- wg.Wait() |
2770 | +- if got := atomic.LoadInt32(&n); got != reqs { |
2771 | +- t.Errorf("handler ran %d times; want %d", got, reqs) |
2772 | +- } |
2773 | +-} |
2774 | +- |
2775 | -func TestServerConnStateNew(t *testing.T) { |
2776 | - sawNew := false // if the test is buggy, we'll race on this variable. |
2777 | - srv := &Server{ |
2778 | @@ -2699,9 +2783,9 @@ |
2779 | -} |
2780 | |
2781 | === modified file 'http13client/server.go' |
2782 | ---- http13client/server.go 2014-03-19 20:20:19 +0000 |
2783 | -+++ http13client/server.go 2014-03-19 22:27:37 +0000 |
2784 | -@@ -2,1984 +2,17 @@ |
2785 | +--- http13client/server.go 2014-06-20 11:00:47 +0000 |
2786 | ++++ http13client/server.go 2014-06-20 12:05:53 +0000 |
2787 | +@@ -2,1976 +2,16 @@ |
2788 | // Use of this source code is governed by a BSD-style |
2789 | // license that can be found in the LICENSE file. |
2790 | |
2791 | @@ -2723,7 +2807,7 @@ |
2792 | - "path" |
2793 | - "runtime" |
2794 | - "strconv" |
2795 | - "strings" |
2796 | +- "strings" |
2797 | "sync" |
2798 | - "sync/atomic" |
2799 | - "time" |
2800 | @@ -3506,18 +3590,16 @@ |
2801 | - } |
2802 | - |
2803 | - code := w.status |
2804 | -- if !bodyAllowedForStatus(code) { |
2805 | -- // Must not have body. |
2806 | -- // RFC 2616 section 10.3.5: "the response MUST NOT include other entity-headers" |
2807 | -- for _, k := range []string{"Content-Type", "Content-Length", "Transfer-Encoding"} { |
2808 | -- delHeader(k) |
2809 | -- } |
2810 | -- } else { |
2811 | +- if bodyAllowedForStatus(code) { |
2812 | - // If no content type, apply sniffing algorithm to body. |
2813 | - _, haveType := header["Content-Type"] |
2814 | - if !haveType { |
2815 | - setHeader.contentType = DetectContentType(p) |
2816 | - } |
2817 | +- } else { |
2818 | +- for _, k := range suppressedHeaders(code) { |
2819 | +- delHeader(k) |
2820 | +- } |
2821 | - } |
2822 | - |
2823 | - if _, ok := header["Date"]; !ok { |
2824 | @@ -3865,16 +3947,10 @@ |
2825 | - // Expect 100 Continue support |
2826 | - req := w.req |
2827 | - if req.expectsContinue() { |
2828 | -- if req.ProtoAtLeast(1, 1) { |
2829 | +- if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 { |
2830 | - // Wrap the Body reader with one that replies on the connection |
2831 | - req.Body = &expectContinueReader{readCloser: req.Body, resp: w} |
2832 | - } |
2833 | -- if req.ContentLength == 0 { |
2834 | -- w.Header().Set("Connection", "close") |
2835 | -- w.WriteHeader(StatusBadRequest) |
2836 | -- w.finishRequest() |
2837 | -- break |
2838 | -- } |
2839 | - req.Header.Del("Expect") |
2840 | - } else if req.Header.get("Expect") != "" { |
2841 | - w.sendExpectationFailed() |
2842 | @@ -4685,11 +4761,11 @@ |
2843 | -} |
2844 | +) |
2845 | |
2846 | - // eofReader is a non-nil io.ReadCloser that always returns EOF. |
2847 | - // It embeds a *strings.Reader so it still has a WriteTo method |
2848 | -@@ -1992,28 +25,6 @@ |
2849 | - ioutil.NopCloser(nil), |
2850 | - } |
2851 | + type eofReaderWithWriteTo struct{} |
2852 | + |
2853 | +@@ -1991,28 +31,6 @@ |
2854 | + // Verify that an io.Copy from an eofReader won't require a buffer. |
2855 | + var _ io.WriterTo = eofReader |
2856 | |
2857 | -// initNPNRequest is an HTTP handler that initializes certain |
2858 | -// uninitialized fields in its *Request. Such partially-initialized |
2859 | |
2860 | === modified file 'http13client/_patches/fix_code.patch' |
2861 | --- http13client/_patches/fix_code.patch 2014-03-19 23:13:58 +0000 |
2862 | +++ http13client/_patches/fix_code.patch 2014-07-02 13:12:36 +0000 |
2863 | @@ -1,6 +1,6 @@ |
2864 | === modified file 'http13client/client.go' |
2865 | ---- http13client/client.go 2014-03-19 20:20:19 +0000 |
2866 | -+++ http13client/client.go 2014-03-19 22:27:37 +0000 |
2867 | +--- http13client/client.go 2014-06-20 11:00:47 +0000 |
2868 | ++++ http13client/client.go 2014-06-20 12:05:53 +0000 |
2869 | @@ -17,6 +17,7 @@ |
2870 | "io/ioutil" |
2871 | "log" |
2872 | @@ -18,7 +18,7 @@ |
2873 | |
2874 | // Timeout specifies a time limit for requests made by this |
2875 | // Client. The timeout includes connection time, any |
2876 | -@@ -177,7 +178,7 @@ |
2877 | +@@ -184,7 +185,7 @@ |
2878 | // Headers, leaving it uninitialized. We guarantee to the |
2879 | // Transport that this has been initialized, though. |
2880 | if req.Header == nil { |
2881 | @@ -27,7 +27,7 @@ |
2882 | } |
2883 | |
2884 | if u := req.URL.User; u != nil { |
2885 | -@@ -308,7 +309,7 @@ |
2886 | +@@ -316,7 +317,7 @@ |
2887 | if ireq.Method == "POST" || ireq.Method == "PUT" { |
2888 | nreq.Method = "GET" |
2889 | } |
2890 | @@ -38,8 +38,8 @@ |
2891 | break |
2892 | |
2893 | === modified file 'http13client/cookie.go' |
2894 | ---- http13client/cookie.go 2014-03-19 20:20:19 +0000 |
2895 | -+++ http13client/cookie.go 2014-03-19 22:27:37 +0000 |
2896 | +--- http13client/cookie.go 2014-06-20 11:00:47 +0000 |
2897 | ++++ http13client/cookie.go 2014-06-20 12:05:53 +0000 |
2898 | @@ -5,10 +5,9 @@ |
2899 | package http |
2900 | |
2901 | @@ -94,7 +94,7 @@ |
2902 | Name: name, |
2903 | Value: value, |
2904 | Raw: line, |
2905 | -@@ -129,59 +108,12 @@ |
2906 | +@@ -125,59 +104,12 @@ |
2907 | return cookies |
2908 | } |
2909 | |
2910 | @@ -156,7 +156,7 @@ |
2911 | lines, ok := h["Cookie"] |
2912 | if !ok { |
2913 | return cookies |
2914 | -@@ -213,7 +145,7 @@ |
2915 | +@@ -209,7 +141,7 @@ |
2916 | if !success { |
2917 | continue |
2918 | } |
2919 | @@ -167,8 +167,8 @@ |
2920 | } |
2921 | |
2922 | === modified file 'http13client/header.go' |
2923 | ---- http13client/header.go 2014-03-19 20:20:19 +0000 |
2924 | -+++ http13client/header.go 2014-03-19 22:27:37 +0000 |
2925 | +--- http13client/header.go 2014-06-20 11:00:47 +0000 |
2926 | ++++ http13client/header.go 2014-06-20 12:00:22 +0000 |
2927 | @@ -5,176 +5,9 @@ |
2928 | package http |
2929 | |
2930 | @@ -348,8 +348,8 @@ |
2931 | // token must be all lowercase. |
2932 | |
2933 | === modified file 'http13client/request.go' |
2934 | ---- http13client/request.go 2014-03-19 20:20:19 +0000 |
2935 | -+++ http13client/request.go 2014-03-19 22:27:37 +0000 |
2936 | +--- http13client/request.go 2014-06-20 11:00:47 +0000 |
2937 | ++++ http13client/request.go 2014-06-20 12:05:53 +0000 |
2938 | @@ -16,6 +16,7 @@ |
2939 | "io/ioutil" |
2940 | "mime" |
2941 | @@ -358,25 +358,25 @@ |
2942 | "net/textproto" |
2943 | "net/url" |
2944 | "strconv" |
2945 | -@@ -103,7 +104,7 @@ |
2946 | - // The request parser implements this by canonicalizing the |
2947 | - // name, making the first character and any characters |
2948 | - // following a hyphen uppercase and the rest lowercase. |
2949 | +@@ -121,7 +122,7 @@ |
2950 | + // added and may override values in Header. |
2951 | + // |
2952 | + // See the documentation for the Request.Write method. |
2953 | - Header Header |
2954 | + Header http.Header |
2955 | |
2956 | // Body is the request's body. |
2957 | // |
2958 | -@@ -164,7 +165,7 @@ |
2959 | - // For server requests, Trailer is only populated after Body has been |
2960 | - // closed or fully consumed. |
2961 | - // Trailer support is only partially complete. |
2962 | +@@ -199,7 +200,7 @@ |
2963 | + // not mutate Trailer. |
2964 | + // |
2965 | + // Few HTTP clients, servers, or proxies support HTTP trailers. |
2966 | - Trailer Header |
2967 | + Trailer http.Header |
2968 | |
2969 | // RemoteAddr allows HTTP servers and other software to record |
2970 | // the network address that sent the request, usually for |
2971 | -@@ -204,7 +205,7 @@ |
2972 | +@@ -239,7 +240,7 @@ |
2973 | } |
2974 | |
2975 | // Cookies parses and returns the HTTP cookies sent with the request. |
2976 | @@ -385,7 +385,7 @@ |
2977 | return readCookies(r.Header, "") |
2978 | } |
2979 | |
2980 | -@@ -212,7 +213,7 @@ |
2981 | +@@ -247,7 +248,7 @@ |
2982 | |
2983 | // Cookie returns the named cookie provided in the request or |
2984 | // ErrNoCookie if not found. |
2985 | @@ -394,7 +394,7 @@ |
2986 | for _, c := range readCookies(r.Header, name) { |
2987 | return c, nil |
2988 | } |
2989 | -@@ -223,7 +224,7 @@ |
2990 | +@@ -258,7 +259,7 @@ |
2991 | // AddCookie does not attach more than one Cookie header field. That |
2992 | // means all cookies, if any, are written into the same line, |
2993 | // separated by semicolon. |
2994 | @@ -403,7 +403,7 @@ |
2995 | s := fmt.Sprintf("%s=%s", sanitizeCookieName(c.Name), sanitizeCookieValue(c.Value)) |
2996 | if c := r.Header.Get("Cookie"); c != "" { |
2997 | r.Header.Set("Cookie", c+"; "+s) |
2998 | -@@ -326,7 +327,7 @@ |
2999 | +@@ -361,7 +362,7 @@ |
3000 | } |
3001 | |
3002 | // extraHeaders may be nil |
3003 | @@ -412,7 +412,7 @@ |
3004 | host := req.Host |
3005 | if host == "" { |
3006 | if req.URL == nil { |
3007 | -@@ -456,7 +457,7 @@ |
3008 | +@@ -490,7 +491,7 @@ |
3009 | Proto: "HTTP/1.1", |
3010 | ProtoMajor: 1, |
3011 | ProtoMinor: 1, |
3012 | @@ -421,7 +421,7 @@ |
3013 | Body: rc, |
3014 | Host: u.Host, |
3015 | } |
3016 | -@@ -571,7 +572,7 @@ |
3017 | +@@ -605,7 +606,7 @@ |
3018 | if err != nil { |
3019 | return nil, err |
3020 | } |
3021 | @@ -430,7 +430,7 @@ |
3022 | |
3023 | // RFC2616: Must treat |
3024 | // GET /index.html HTTP/1.1 |
3025 | -@@ -582,7 +583,7 @@ |
3026 | +@@ -616,7 +617,7 @@ |
3027 | // the same. In the second case, any Host line is ignored. |
3028 | req.Host = req.URL.Host |
3029 | if req.Host == "" { |
3030 | @@ -439,7 +439,7 @@ |
3031 | } |
3032 | delete(req.Header, "Host") |
3033 | |
3034 | -@@ -630,12 +631,12 @@ |
3035 | +@@ -638,12 +639,12 @@ |
3036 | // |
3037 | // MaxBytesReader prevents clients from accidentally or maliciously |
3038 | // sending a large request and wasting server resources. |
3039 | @@ -454,7 +454,7 @@ |
3040 | r io.ReadCloser // underlying reader |
3041 | n int64 // max bytes remaining |
3042 | stopped bool |
3043 | -@@ -645,9 +646,6 @@ |
3044 | +@@ -653,9 +654,6 @@ |
3045 | if l.n <= 0 { |
3046 | if !l.stopped { |
3047 | l.stopped = true |
3048 | @@ -464,7 +464,7 @@ |
3049 | } |
3050 | return 0, errors.New("http: request body too large") |
3051 | } |
3052 | -@@ -852,16 +850,16 @@ |
3053 | +@@ -858,18 +856,18 @@ |
3054 | } |
3055 | |
3056 | func (r *Request) expectsContinue() bool { |
3057 | @@ -484,11 +484,13 @@ |
3058 | - return hasToken(r.Header.get("Connection"), "close") |
3059 | + return hasToken(r.Header.Get("Connection"), "close") |
3060 | } |
3061 | + |
3062 | + func (r *Request) closeBody() { |
3063 | |
3064 | === modified file 'http13client/response.go' |
3065 | ---- http13client/response.go 2014-03-19 20:20:19 +0000 |
3066 | -+++ http13client/response.go 2014-03-19 22:27:37 +0000 |
3067 | -@@ -11,6 +11,7 @@ |
3068 | +--- http13client/response.go 2014-06-20 11:00:47 +0000 |
3069 | ++++ http13client/response.go 2014-06-20 12:05:53 +0000 |
3070 | +@@ -12,6 +12,7 @@ |
3071 | "crypto/tls" |
3072 | "errors" |
3073 | "io" |
3074 | @@ -496,7 +498,7 @@ |
3075 | "net/textproto" |
3076 | "net/url" |
3077 | "strconv" |
3078 | -@@ -40,7 +41,7 @@ |
3079 | +@@ -41,7 +42,7 @@ |
3080 | // omitted from Header. |
3081 | // |
3082 | // Keys in the map are canonicalized (see CanonicalHeaderKey). |
3083 | @@ -505,7 +507,7 @@ |
3084 | |
3085 | // Body represents the response body. |
3086 | // |
3087 | -@@ -69,7 +70,7 @@ |
3088 | +@@ -71,7 +72,7 @@ |
3089 | |
3090 | // Trailer maps trailer keys to values, in the same |
3091 | // format as the header. |
3092 | @@ -514,7 +516,7 @@ |
3093 | |
3094 | // The Request that was sent to obtain this Response. |
3095 | // Request's Body is nil (having already been consumed). |
3096 | -@@ -84,7 +85,7 @@ |
3097 | +@@ -86,7 +87,7 @@ |
3098 | } |
3099 | |
3100 | // Cookies parses and returns the cookies set in the Set-Cookie headers. |
3101 | @@ -523,7 +525,7 @@ |
3102 | return readSetCookies(r.Header) |
3103 | } |
3104 | |
3105 | -@@ -153,7 +154,7 @@ |
3106 | +@@ -155,7 +156,7 @@ |
3107 | } |
3108 | return nil, err |
3109 | } |
3110 | @@ -532,7 +534,7 @@ |
3111 | |
3112 | fixPragmaCacheControl(resp.Header) |
3113 | |
3114 | -@@ -169,7 +170,7 @@ |
3115 | +@@ -171,7 +172,7 @@ |
3116 | // Pragma: no-cache |
3117 | // like |
3118 | // Cache-Control: no-cache |
3119 | @@ -543,17 +545,17 @@ |
3120 | header["Cache-Control"] = []string{"no-cache"} |
3121 | |
3122 | === modified file 'http13client/transfer.go' |
3123 | ---- http13client/transfer.go 2014-03-19 20:20:19 +0000 |
3124 | -+++ http13client/transfer.go 2014-03-19 22:27:37 +0000 |
3125 | +--- http13client/transfer.go 2014-06-20 11:00:47 +0000 |
3126 | ++++ http13client/transfer.go 2014-06-20 12:05:53 +0000 |
3127 | @@ -11,6 +11,7 @@ |
3128 | "fmt" |
3129 | "io" |
3130 | "io/ioutil" |
3131 | + "net/http" |
3132 | "net/textproto" |
3133 | + "sort" |
3134 | "strconv" |
3135 | - "strings" |
3136 | -@@ -36,7 +37,7 @@ |
3137 | +@@ -37,7 +38,7 @@ |
3138 | ContentLength int64 // -1 means unknown, 0 means exactly none |
3139 | Close bool |
3140 | TransferEncoding []string |
3141 | @@ -562,16 +564,16 @@ |
3142 | } |
3143 | |
3144 | func newTransferWriter(r interface{}) (t *transferWriter, err error) { |
3145 | -@@ -174,7 +175,7 @@ |
3146 | - io.WriteString(w, "Trailer: ") |
3147 | - needComma := false |
3148 | +@@ -171,7 +172,7 @@ |
3149 | + if t.Trailer != nil { |
3150 | + keys := make([]string, 0, len(t.Trailer)) |
3151 | for k := range t.Trailer { |
3152 | - k = CanonicalHeaderKey(k) |
3153 | + k = http.CanonicalHeaderKey(k) |
3154 | switch k { |
3155 | case "Transfer-Encoding", "Trailer", "Content-Length": |
3156 | return &badStringError{"invalid Trailer key", k} |
3157 | -@@ -237,7 +238,7 @@ |
3158 | +@@ -243,7 +244,7 @@ |
3159 | |
3160 | type transferReader struct { |
3161 | // Input |
3162 | @@ -580,7 +582,7 @@ |
3163 | StatusCode int |
3164 | RequestMethod string |
3165 | ProtoMajor int |
3166 | -@@ -247,7 +248,7 @@ |
3167 | +@@ -253,7 +254,7 @@ |
3168 | ContentLength int64 |
3169 | TransferEncoding []string |
3170 | Close bool |
3171 | @@ -589,7 +591,7 @@ |
3172 | } |
3173 | |
3174 | // bodyAllowedForStatus reports whether a given response status code |
3175 | -@@ -308,7 +309,7 @@ |
3176 | +@@ -330,7 +331,7 @@ |
3177 | return err |
3178 | } |
3179 | if isResponse && t.RequestMethod == "HEAD" { |
3180 | @@ -598,7 +600,7 @@ |
3181 | return err |
3182 | } else { |
3183 | t.ContentLength = n |
3184 | -@@ -386,7 +387,7 @@ |
3185 | +@@ -408,7 +409,7 @@ |
3186 | func isIdentity(te []string) bool { return len(te) == 1 && te[0] == "identity" } |
3187 | |
3188 | // Sanitize transfer encoding |
3189 | @@ -607,7 +609,7 @@ |
3190 | raw, present := header["Transfer-Encoding"] |
3191 | if !present { |
3192 | return nil, nil |
3193 | -@@ -429,7 +430,7 @@ |
3194 | +@@ -451,7 +452,7 @@ |
3195 | // Determine the expected body length, using RFC 2616 Section 4.4. This |
3196 | // function is not a method, because ultimately it should be shared by |
3197 | // ReadResponse and ReadRequest. |
3198 | @@ -616,7 +618,7 @@ |
3199 | |
3200 | // Logic based on response type or status |
3201 | if noBodyExpected(requestMethod) { |
3202 | -@@ -449,7 +450,7 @@ |
3203 | +@@ -471,7 +472,7 @@ |
3204 | } |
3205 | |
3206 | // Logic based on Content-Length |
3207 | @@ -625,7 +627,7 @@ |
3208 | if cl != "" { |
3209 | n, err := parseContentLength(cl) |
3210 | if err != nil { |
3211 | -@@ -475,18 +476,18 @@ |
3212 | +@@ -497,18 +498,18 @@ |
3213 | // Determine whether to hang up after sending a request and body, or |
3214 | // receiving a response and body |
3215 | // 'header' is the request headers |
3216 | @@ -647,7 +649,7 @@ |
3217 | header.Del("Connection") |
3218 | return true |
3219 | } |
3220 | -@@ -495,17 +496,17 @@ |
3221 | +@@ -517,17 +518,17 @@ |
3222 | } |
3223 | |
3224 | // Parse the trailer header |
3225 | @@ -669,22 +671,28 @@ |
3226 | switch key { |
3227 | case "Transfer-Encoding", "Trailer", "Content-Length": |
3228 | return nil, &badStringError{"bad trailer key", key} |
3229 | -@@ -642,9 +643,9 @@ |
3230 | +@@ -664,14 +665,14 @@ |
3231 | } |
3232 | switch rr := b.hdr.(type) { |
3233 | case *Request: |
3234 | -- rr.Trailer = Header(hdr) |
3235 | -+ rr.Trailer = http.Header(hdr) |
3236 | +- mergeSetHeader(&rr.Trailer, Header(hdr)) |
3237 | ++ mergeSetHeader(&rr.Trailer, http.Header(hdr)) |
3238 | case *Response: |
3239 | -- rr.Trailer = Header(hdr) |
3240 | -+ rr.Trailer = http.Header(hdr) |
3241 | +- mergeSetHeader(&rr.Trailer, Header(hdr)) |
3242 | ++ mergeSetHeader(&rr.Trailer, http.Header(hdr)) |
3243 | } |
3244 | return nil |
3245 | } |
3246 | + |
3247 | +-func mergeSetHeader(dst *Header, src Header) { |
3248 | ++func mergeSetHeader(dst *http.Header, src http.Header) { |
3249 | + if *dst == nil { |
3250 | + *dst = src |
3251 | + return |
3252 | |
3253 | === modified file 'http13client/transport.go' |
3254 | ---- http13client/transport.go 2014-03-19 20:20:19 +0000 |
3255 | -+++ http13client/transport.go 2014-03-19 22:27:37 +0000 |
3256 | +--- http13client/transport.go 2014-06-20 11:00:47 +0000 |
3257 | ++++ http13client/transport.go 2014-06-20 12:05:53 +0000 |
3258 | @@ -18,6 +18,7 @@ |
3259 | "io" |
3260 | "log" |
3261 | @@ -693,7 +701,7 @@ |
3262 | "net/url" |
3263 | "os" |
3264 | "strings" |
3265 | -@@ -144,12 +145,12 @@ |
3266 | +@@ -147,12 +148,12 @@ |
3267 | // optional extra headers to write. |
3268 | type transportRequest struct { |
3269 | *Request // original request, not to be mutated |
3270 | @@ -709,7 +717,7 @@ |
3271 | } |
3272 | return tr.extra |
3273 | } |
3274 | -@@ -512,7 +513,7 @@ |
3275 | +@@ -519,7 +520,7 @@ |
3276 | case cm.targetScheme == "http": |
3277 | pconn.isProxy = true |
3278 | if pa != "" { |
3279 | @@ -718,7 +726,7 @@ |
3280 | h.Set("Proxy-Authorization", pa) |
3281 | } |
3282 | } |
3283 | -@@ -521,7 +522,7 @@ |
3284 | +@@ -528,7 +529,7 @@ |
3285 | Method: "CONNECT", |
3286 | URL: &url.URL{Opaque: cm.targetAddr}, |
3287 | Host: cm.targetAddr, |
3288 | @@ -727,7 +735,7 @@ |
3289 | } |
3290 | if pa != "" { |
3291 | connectReq.Header.Set("Proxy-Authorization", pa) |
3292 | -@@ -735,7 +736,7 @@ |
3293 | +@@ -748,7 +749,7 @@ |
3294 | // mutateHeaderFunc is an optional func to modify extra |
3295 | // headers on each outbound request before it's written. (the |
3296 | // original Request given to RoundTrip is not modified) |
3297 | @@ -735,5 +743,5 @@ |
3298 | + mutateHeaderFunc func(http.Header) |
3299 | } |
3300 | |
3301 | - func (pc *persistConn) isBroken() bool { |
3302 | + // isBroken reports whether this connection is in a known broken state. |
3303 | |
3304 | |
3305 | === modified file 'http13client/_patches/fix_status.patch' |
3306 | --- http13client/_patches/fix_status.patch 2014-03-19 23:43:25 +0000 |
3307 | +++ http13client/_patches/fix_status.patch 2014-07-02 13:12:36 +0000 |
3308 | @@ -1,7 +1,7 @@ |
3309 | === modified file 'http13client/client.go' |
3310 | ---- http13client/client.go 2014-03-19 23:13:58 +0000 |
3311 | -+++ http13client/client.go 2014-03-19 23:38:11 +0000 |
3312 | -@@ -210,7 +210,7 @@ |
3313 | +--- http13client/client.go 2014-06-20 12:46:25 +0000 |
3314 | ++++ http13client/client.go 2014-06-20 12:46:45 +0000 |
3315 | +@@ -217,7 +217,7 @@ |
3316 | // automatically redirect. |
3317 | func shouldRedirectGet(statusCode int) bool { |
3318 | switch statusCode { |
3319 | @@ -10,7 +10,7 @@ |
3320 | return true |
3321 | } |
3322 | return false |
3323 | -@@ -220,7 +220,7 @@ |
3324 | +@@ -227,7 +227,7 @@ |
3325 | // automatically redirect. |
3326 | func shouldRedirectPost(statusCode int) bool { |
3327 | switch statusCode { |
3328 | @@ -21,9 +21,9 @@ |
3329 | return false |
3330 | |
3331 | === modified file 'http13client/client_test.go' |
3332 | ---- http13client/client_test.go 2014-03-19 23:13:58 +0000 |
3333 | -+++ http13client/client_test.go 2014-03-19 23:39:48 +0000 |
3334 | -@@ -202,7 +202,7 @@ |
3335 | +--- http13client/client_test.go 2014-06-20 12:46:25 +0000 |
3336 | ++++ http13client/client_test.go 2014-06-20 12:46:45 +0000 |
3337 | +@@ -204,7 +204,7 @@ |
3338 | } |
3339 | } |
3340 | if n < 15 { |
3341 | @@ -32,7 +32,7 @@ |
3342 | return |
3343 | } |
3344 | fmt.Fprintf(w, "n=%d", n) |
3345 | -@@ -324,7 +324,7 @@ |
3346 | +@@ -326,7 +326,7 @@ |
3347 | } |
3348 | if r.URL.Path == "/" { |
3349 | http.SetCookie(w, expectedCookies[1]) |
3350 | @@ -41,7 +41,7 @@ |
3351 | } else { |
3352 | http.SetCookie(w, expectedCookies[2]) |
3353 | w.Write([]byte("hello")) |
3354 | -@@ -783,7 +783,7 @@ |
3355 | +@@ -785,7 +785,7 @@ |
3356 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
3357 | if r.URL.Path == "/" { |
3358 | sawRoot <- true |
3359 | @@ -50,7 +50,7 @@ |
3360 | return |
3361 | } |
3362 | if r.URL.Path == "/slow" { |
3363 | -@@ -844,7 +844,7 @@ |
3364 | +@@ -846,7 +846,7 @@ |
3365 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
3366 | saw <- r.RemoteAddr |
3367 | if r.URL.Path == "/" { |
3368 | @@ -61,9 +61,9 @@ |
3369 | defer ts.Close() |
3370 | |
3371 | === modified file 'http13client/request_test.go' |
3372 | ---- http13client/request_test.go 2014-03-19 23:13:58 +0000 |
3373 | -+++ http13client/request_test.go 2014-03-19 23:40:12 +0000 |
3374 | -@@ -164,11 +164,11 @@ |
3375 | +--- http13client/request_test.go 2014-06-20 12:46:25 +0000 |
3376 | ++++ http13client/request_test.go 2014-06-20 12:46:45 +0000 |
3377 | +@@ -182,11 +182,11 @@ |
3378 | switch r.URL.Path { |
3379 | case "/": |
3380 | w.Header().Set("Location", "/foo/") |
3381 | @@ -79,9 +79,9 @@ |
3382 | defer ts.Close() |
3383 | |
3384 | === modified file 'http13client/response.go' |
3385 | ---- http13client/response.go 2014-03-19 23:13:58 +0000 |
3386 | -+++ http13client/response.go 2014-03-19 23:38:56 +0000 |
3387 | -@@ -204,9 +204,8 @@ |
3388 | +--- http13client/response.go 2014-06-20 12:46:25 +0000 |
3389 | ++++ http13client/response.go 2014-06-20 12:46:45 +0000 |
3390 | +@@ -205,9 +205,8 @@ |
3391 | // Status line |
3392 | text := r.Status |
3393 | if text == "" { |
3394 | @@ -94,10 +94,23 @@ |
3395 | } |
3396 | } |
3397 | |
3398 | +=== modified file 'http13client/responsewrite_test.go' |
3399 | +--- http13client/responsewrite_test.go 2014-06-20 12:46:25 +0000 |
3400 | ++++ http13client/responsewrite_test.go 2014-06-20 12:47:05 +0000 |
3401 | +@@ -197,7 +197,7 @@ |
3402 | + // there were two. |
3403 | + { |
3404 | + Response{ |
3405 | +- StatusCode: StatusOK, |
3406 | ++ StatusCode: http.StatusOK, |
3407 | + ProtoMajor: 1, |
3408 | + ProtoMinor: 1, |
3409 | + Request: &Request{Method: "POST"}, |
3410 | + |
3411 | === modified file 'http13client/transport_test.go' |
3412 | ---- http13client/transport_test.go 2014-03-19 23:13:58 +0000 |
3413 | -+++ http13client/transport_test.go 2014-03-19 23:40:39 +0000 |
3414 | -@@ -968,7 +968,7 @@ |
3415 | +--- http13client/transport_test.go 2014-06-20 12:46:25 +0000 |
3416 | ++++ http13client/transport_test.go 2014-06-20 12:46:45 +0000 |
3417 | +@@ -1004,7 +1004,7 @@ |
3418 | defer afterTest(t) |
3419 | const deniedMsg = "sorry, denied." |
3420 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
3421 | @@ -106,7 +119,7 @@ |
3422 | })) |
3423 | defer ts.Close() |
3424 | tr := &Transport{} |
3425 | -@@ -992,7 +992,7 @@ |
3426 | +@@ -1028,7 +1028,7 @@ |
3427 | func TestChunkedNoContent(t *testing.T) { |
3428 | defer afterTest(t) |
3429 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
3430 | |
3431 | === modified file 'http13client/_patches/fix_tests.patch' |
3432 | --- http13client/_patches/fix_tests.patch 2014-03-19 23:13:58 +0000 |
3433 | +++ http13client/_patches/fix_tests.patch 2014-07-02 13:12:36 +0000 |
3434 | @@ -1,6 +1,6 @@ |
3435 | === modified file 'http13client/client_test.go' |
3436 | ---- http13client/client_test.go 2014-03-19 21:38:56 +0000 |
3437 | -+++ http13client/client_test.go 2014-03-19 22:27:37 +0000 |
3438 | +--- http13client/client_test.go 2014-06-20 11:00:47 +0000 |
3439 | ++++ http13client/client_test.go 2014-06-20 12:05:53 +0000 |
3440 | @@ -15,8 +15,8 @@ |
3441 | "fmt" |
3442 | "io" |
3443 | @@ -11,7 +11,7 @@ |
3444 | . "launchpad.net/ubuntu-push/http13client" |
3445 | "net/http/httptest" |
3446 | "net/url" |
3447 | -@@ -27,7 +27,7 @@ |
3448 | +@@ -29,7 +29,7 @@ |
3449 | "time" |
3450 | ) |
3451 | |
3452 | @@ -20,7 +20,7 @@ |
3453 | w.Header().Set("Last-Modified", "sometime") |
3454 | fmt.Fprintf(w, "User-agent: go\nDisallow: /something/") |
3455 | }) |
3456 | -@@ -193,7 +193,7 @@ |
3457 | +@@ -195,7 +195,7 @@ |
3458 | func TestClientRedirects(t *testing.T) { |
3459 | defer afterTest(t) |
3460 | var ts *httptest.Server |
3461 | @@ -29,7 +29,7 @@ |
3462 | n, _ := strconv.Atoi(r.FormValue("n")) |
3463 | // Test Referer header. (7 is arbitrary position to test at) |
3464 | if n == 7 { |
3465 | -@@ -202,7 +202,7 @@ |
3466 | +@@ -204,7 +204,7 @@ |
3467 | } |
3468 | } |
3469 | if n < 15 { |
3470 | @@ -38,7 +38,7 @@ |
3471 | return |
3472 | } |
3473 | fmt.Fprintf(w, "n=%d", n) |
3474 | -@@ -271,7 +271,7 @@ |
3475 | +@@ -273,7 +273,7 @@ |
3476 | bytes.Buffer |
3477 | } |
3478 | var ts *httptest.Server |
3479 | @@ -47,7 +47,7 @@ |
3480 | log.Lock() |
3481 | fmt.Fprintf(&log.Buffer, "%s %s ", r.Method, r.RequestURI) |
3482 | log.Unlock() |
3483 | -@@ -312,21 +312,21 @@ |
3484 | +@@ -314,21 +314,21 @@ |
3485 | } |
3486 | } |
3487 | |
3488 | @@ -75,7 +75,7 @@ |
3489 | w.Write([]byte("hello")) |
3490 | } |
3491 | }) |
3492 | -@@ -334,7 +334,7 @@ |
3493 | +@@ -336,7 +336,7 @@ |
3494 | func TestClientSendsCookieFromJar(t *testing.T) { |
3495 | tr := &recordingTransport{} |
3496 | client := &Client{Transport: tr} |
3497 | @@ -84,7 +84,7 @@ |
3498 | us := "http://dummy.faketld/" |
3499 | u, _ := url.Parse(us) |
3500 | client.Jar.SetCookies(u, expectedCookies) |
3501 | -@@ -364,19 +364,19 @@ |
3502 | +@@ -366,19 +366,19 @@ |
3503 | // scope of all cookies. |
3504 | type TestJar struct { |
3505 | m sync.Mutex |
3506 | @@ -108,7 +108,7 @@ |
3507 | j.m.Lock() |
3508 | defer j.m.Unlock() |
3509 | return j.perURL[u.Host] |
3510 | -@@ -391,7 +391,7 @@ |
3511 | +@@ -393,7 +393,7 @@ |
3512 | Jar: new(TestJar), |
3513 | } |
3514 | u, _ := url.Parse(ts.URL) |
3515 | @@ -117,7 +117,7 @@ |
3516 | resp, err := c.Get(ts.URL) |
3517 | if err != nil { |
3518 | t.Fatalf("Get: %v", err) |
3519 | -@@ -400,7 +400,7 @@ |
3520 | +@@ -402,7 +402,7 @@ |
3521 | matchReturnedCookies(t, expectedCookies, resp.Cookies()) |
3522 | } |
3523 | |
3524 | @@ -126,7 +126,7 @@ |
3525 | if len(given) != len(expected) { |
3526 | t.Logf("Received cookies: %v", given) |
3527 | t.Errorf("Expected %d cookies, got %d", len(expected), len(given)) |
3528 | -@@ -421,14 +421,14 @@ |
3529 | +@@ -423,14 +423,14 @@ |
3530 | |
3531 | func TestJarCalls(t *testing.T) { |
3532 | defer afterTest(t) |
3533 | @@ -144,7 +144,7 @@ |
3534 | } |
3535 | })) |
3536 | defer ts.Close() |
3537 | -@@ -468,11 +468,11 @@ |
3538 | +@@ -470,11 +470,11 @@ |
3539 | log bytes.Buffer |
3540 | } |
3541 | |
3542 | @@ -158,7 +158,7 @@ |
3543 | j.logf("Cookies(%q)\n", u) |
3544 | return nil |
3545 | } |
3546 | -@@ -486,11 +486,11 @@ |
3547 | +@@ -488,11 +488,11 @@ |
3548 | func TestStreamingGet(t *testing.T) { |
3549 | defer afterTest(t) |
3550 | say := make(chan string) |
3551 | @@ -173,7 +173,7 @@ |
3552 | } |
3553 | })) |
3554 | defer ts.Close() |
3555 | -@@ -536,7 +536,7 @@ |
3556 | +@@ -538,7 +538,7 @@ |
3557 | // don't send a TCP packet per line of the http request + body. |
3558 | func TestClientWrites(t *testing.T) { |
3559 | defer afterTest(t) |
3560 | @@ -182,7 +182,7 @@ |
3561 | })) |
3562 | defer ts.Close() |
3563 | |
3564 | -@@ -568,46 +568,6 @@ |
3565 | +@@ -570,46 +570,6 @@ |
3566 | } |
3567 | } |
3568 | |
3569 | @@ -229,7 +229,7 @@ |
3570 | func TestClientErrorWithRequestURI(t *testing.T) { |
3571 | defer afterTest(t) |
3572 | req, _ := NewRequest("GET", "http://localhost:1234/", nil) |
3573 | -@@ -639,7 +599,7 @@ |
3574 | +@@ -641,7 +601,7 @@ |
3575 | |
3576 | func TestClientWithCorrectTLSServerName(t *testing.T) { |
3577 | defer afterTest(t) |
3578 | @@ -238,7 +238,7 @@ |
3579 | if r.TLS.ServerName != "127.0.0.1" { |
3580 | t.Errorf("expected client to set ServerName 127.0.0.1, got: %q", r.TLS.ServerName) |
3581 | } |
3582 | -@@ -652,33 +612,6 @@ |
3583 | +@@ -654,33 +614,6 @@ |
3584 | } |
3585 | } |
3586 | |
3587 | @@ -272,7 +272,7 @@ |
3588 | // Test for golang.org/issue/5829; the Transport should respect TLSClientConfig.ServerName |
3589 | // when not empty. |
3590 | // |
3591 | -@@ -690,7 +623,7 @@ |
3592 | +@@ -692,7 +625,7 @@ |
3593 | // The httptest.Server has a cert with "example.com" as its name. |
3594 | func TestTransportUsesTLSConfigServerName(t *testing.T) { |
3595 | defer afterTest(t) |
3596 | @@ -281,7 +281,7 @@ |
3597 | w.Write([]byte("Hello")) |
3598 | })) |
3599 | defer ts.Close() |
3600 | -@@ -711,7 +644,7 @@ |
3601 | +@@ -713,7 +646,7 @@ |
3602 | |
3603 | func TestResponseSetsTLSConnectionState(t *testing.T) { |
3604 | defer afterTest(t) |
3605 | @@ -290,7 +290,7 @@ |
3606 | w.Write([]byte("Hello")) |
3607 | })) |
3608 | defer ts.Close() |
3609 | -@@ -739,7 +672,7 @@ |
3610 | +@@ -741,7 +674,7 @@ |
3611 | // Verify Response.ContentLength is populated. http://golang.org/issue/4126 |
3612 | func TestClientHeadContentLength(t *testing.T) { |
3613 | defer afterTest(t) |
3614 | @@ -299,7 +299,7 @@ |
3615 | if v := r.FormValue("cl"); v != "" { |
3616 | w.Header().Set("Content-Length", v) |
3617 | } |
3618 | -@@ -775,7 +708,7 @@ |
3619 | +@@ -777,7 +710,7 @@ |
3620 | func TestEmptyPasswordAuth(t *testing.T) { |
3621 | defer afterTest(t) |
3622 | gopher := "gopher" |
3623 | @@ -308,7 +308,7 @@ |
3624 | auth := r.Header.Get("Authorization") |
3625 | if strings.HasPrefix(auth, "Basic ") { |
3626 | encoded := auth[6:] |
3627 | -@@ -847,15 +780,15 @@ |
3628 | +@@ -849,15 +782,15 @@ |
3629 | defer afterTest(t) |
3630 | sawRoot := make(chan bool, 1) |
3631 | sawSlow := make(chan bool, 1) |
3632 | @@ -327,7 +327,7 @@ |
3633 | sawSlow <- true |
3634 | time.Sleep(2 * time.Second) |
3635 | return |
3636 | -@@ -908,10 +841,10 @@ |
3637 | +@@ -910,10 +843,10 @@ |
3638 | func TestClientRedirectEatsBody(t *testing.T) { |
3639 | defer afterTest(t) |
3640 | saw := make(chan string, 2) |
3641 | @@ -340,233 +340,50 @@ |
3642 | } |
3643 | })) |
3644 | defer ts.Close() |
3645 | - |
3646 | -=== modified file 'http13client/cookie_test.go' |
3647 | ---- http13client/cookie_test.go 2014-03-19 20:20:19 +0000 |
3648 | -+++ http13client/cookie_test.go 2014-03-19 22:27:37 +0000 |
3649 | -@@ -9,6 +9,7 @@ |
3650 | - "encoding/json" |
3651 | - "fmt" |
3652 | - "log" |
3653 | -+ "net/http" |
3654 | - "os" |
3655 | - "reflect" |
3656 | - "strings" |
3657 | -@@ -17,39 +18,39 @@ |
3658 | - ) |
3659 | - |
3660 | - var writeSetCookiesTests = []struct { |
3661 | -- Cookie *Cookie |
3662 | -+ Cookie *http.Cookie |
3663 | - Raw string |
3664 | - }{ |
3665 | - { |
3666 | -- &Cookie{Name: "cookie-1", Value: "v$1"}, |
3667 | -+ &http.Cookie{Name: "cookie-1", Value: "v$1"}, |
3668 | - "cookie-1=v$1", |
3669 | - }, |
3670 | - { |
3671 | -- &Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600}, |
3672 | -+ &http.Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600}, |
3673 | - "cookie-2=two; Max-Age=3600", |
3674 | - }, |
3675 | - { |
3676 | -- &Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"}, |
3677 | -+ &http.Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"}, |
3678 | - "cookie-3=three; Domain=example.com", |
3679 | - }, |
3680 | - { |
3681 | -- &Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"}, |
3682 | -+ &http.Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"}, |
3683 | - "cookie-4=four; Path=/restricted/", |
3684 | - }, |
3685 | - { |
3686 | -- &Cookie{Name: "cookie-5", Value: "five", Domain: "wrong;bad.abc"}, |
3687 | -+ &http.Cookie{Name: "cookie-5", Value: "five", Domain: "wrong;bad.abc"}, |
3688 | - "cookie-5=five", |
3689 | - }, |
3690 | - { |
3691 | -- &Cookie{Name: "cookie-6", Value: "six", Domain: "bad-.abc"}, |
3692 | -+ &http.Cookie{Name: "cookie-6", Value: "six", Domain: "bad-.abc"}, |
3693 | - "cookie-6=six", |
3694 | - }, |
3695 | - { |
3696 | -- &Cookie{Name: "cookie-7", Value: "seven", Domain: "127.0.0.1"}, |
3697 | -+ &http.Cookie{Name: "cookie-7", Value: "seven", Domain: "127.0.0.1"}, |
3698 | - "cookie-7=seven; Domain=127.0.0.1", |
3699 | - }, |
3700 | - { |
3701 | -- &Cookie{Name: "cookie-8", Value: "eight", Domain: "::1"}, |
3702 | -+ &http.Cookie{Name: "cookie-8", Value: "eight", Domain: "::1"}, |
3703 | - "cookie-8=eight", |
3704 | - }, |
3705 | - } |
3706 | -@@ -71,10 +72,10 @@ |
3707 | - } |
3708 | - } |
3709 | - |
3710 | --type headerOnlyResponseWriter Header |
3711 | -+type headerOnlyResponseWriter http.Header |
3712 | - |
3713 | --func (ho headerOnlyResponseWriter) Header() Header { |
3714 | -- return Header(ho) |
3715 | -+func (ho headerOnlyResponseWriter) Header() http.Header { |
3716 | -+ return http.Header(ho) |
3717 | - } |
3718 | - |
3719 | - func (ho headerOnlyResponseWriter) Write([]byte) (int, error) { |
3720 | -@@ -86,9 +87,9 @@ |
3721 | - } |
3722 | - |
3723 | - func TestSetCookie(t *testing.T) { |
3724 | -- m := make(Header) |
3725 | -- SetCookie(headerOnlyResponseWriter(m), &Cookie{Name: "cookie-1", Value: "one", Path: "/restricted/"}) |
3726 | -- SetCookie(headerOnlyResponseWriter(m), &Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600}) |
3727 | -+ m := make(http.Header) |
3728 | -+ http.SetCookie(headerOnlyResponseWriter(m), &http.Cookie{Name: "cookie-1", Value: "one", Path: "/restricted/"}) |
3729 | -+ http.SetCookie(headerOnlyResponseWriter(m), &http.Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600}) |
3730 | - if l := len(m["Set-Cookie"]); l != 2 { |
3731 | - t.Fatalf("expected %d cookies, got %d", 2, l) |
3732 | - } |
3733 | -@@ -101,19 +102,19 @@ |
3734 | - } |
3735 | - |
3736 | - var addCookieTests = []struct { |
3737 | -- Cookies []*Cookie |
3738 | -+ Cookies []*http.Cookie |
3739 | - Raw string |
3740 | - }{ |
3741 | - { |
3742 | -- []*Cookie{}, |
3743 | -+ []*http.Cookie{}, |
3744 | - "", |
3745 | - }, |
3746 | - { |
3747 | -- []*Cookie{{Name: "cookie-1", Value: "v$1"}}, |
3748 | -+ []*http.Cookie{{Name: "cookie-1", Value: "v$1"}}, |
3749 | - "cookie-1=v$1", |
3750 | - }, |
3751 | - { |
3752 | -- []*Cookie{ |
3753 | -+ []*http.Cookie{ |
3754 | - {Name: "cookie-1", Value: "v$1"}, |
3755 | - {Name: "cookie-2", Value: "v$2"}, |
3756 | - {Name: "cookie-3", Value: "v$3"}, |
3757 | -@@ -136,16 +137,16 @@ |
3758 | - } |
3759 | - |
3760 | - var readSetCookiesTests = []struct { |
3761 | -- Header Header |
3762 | -- Cookies []*Cookie |
3763 | -+ Header http.Header |
3764 | -+ Cookies []*http.Cookie |
3765 | - }{ |
3766 | - { |
3767 | -- Header{"Set-Cookie": {"Cookie-1=v$1"}}, |
3768 | -- []*Cookie{{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"}}, |
3769 | -+ http.Header{"Set-Cookie": {"Cookie-1=v$1"}}, |
3770 | -+ []*http.Cookie{{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"}}, |
3771 | - }, |
3772 | - { |
3773 | -- Header{"Set-Cookie": {"NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly"}}, |
3774 | -- []*Cookie{{ |
3775 | -+ http.Header{"Set-Cookie": {"NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly"}}, |
3776 | -+ []*http.Cookie{{ |
3777 | - Name: "NID", |
3778 | - Value: "99=YsDT5i3E-CXax-", |
3779 | - Path: "/", |
3780 | -@@ -157,8 +158,8 @@ |
3781 | - }}, |
3782 | - }, |
3783 | - { |
3784 | -- Header{"Set-Cookie": {".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}}, |
3785 | -- []*Cookie{{ |
3786 | -+ http.Header{"Set-Cookie": {".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}}, |
3787 | -+ []*http.Cookie{{ |
3788 | - Name: ".ASPXAUTH", |
3789 | - Value: "7E3AA", |
3790 | - Path: "/", |
3791 | -@@ -169,8 +170,8 @@ |
3792 | - }}, |
3793 | - }, |
3794 | - { |
3795 | -- Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly"}}, |
3796 | -- []*Cookie{{ |
3797 | -+ http.Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly"}}, |
3798 | -+ []*http.Cookie{{ |
3799 | - Name: "ASP.NET_SessionId", |
3800 | - Value: "foo", |
3801 | - Path: "/", |
3802 | -@@ -207,37 +208,37 @@ |
3803 | - } |
3804 | - |
3805 | - var readCookiesTests = []struct { |
3806 | -- Header Header |
3807 | -+ Header http.Header |
3808 | - Filter string |
3809 | -- Cookies []*Cookie |
3810 | -+ Cookies []*http.Cookie |
3811 | - }{ |
3812 | - { |
3813 | -- Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}}, |
3814 | -- "", |
3815 | -- []*Cookie{ |
3816 | -- {Name: "Cookie-1", Value: "v$1"}, |
3817 | -- {Name: "c2", Value: "v2"}, |
3818 | -- }, |
3819 | -- }, |
3820 | -- { |
3821 | -- Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}}, |
3822 | -- "c2", |
3823 | -- []*Cookie{ |
3824 | -- {Name: "c2", Value: "v2"}, |
3825 | -- }, |
3826 | -- }, |
3827 | -- { |
3828 | -- Header{"Cookie": {"Cookie-1=v$1; c2=v2"}}, |
3829 | -- "", |
3830 | -- []*Cookie{ |
3831 | -- {Name: "Cookie-1", Value: "v$1"}, |
3832 | -- {Name: "c2", Value: "v2"}, |
3833 | -- }, |
3834 | -- }, |
3835 | -- { |
3836 | -- Header{"Cookie": {"Cookie-1=v$1; c2=v2"}}, |
3837 | -- "c2", |
3838 | -- []*Cookie{ |
3839 | -+ http.Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}}, |
3840 | -+ "", |
3841 | -+ []*http.Cookie{ |
3842 | -+ {Name: "Cookie-1", Value: "v$1"}, |
3843 | -+ {Name: "c2", Value: "v2"}, |
3844 | -+ }, |
3845 | -+ }, |
3846 | -+ { |
3847 | -+ http.Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}}, |
3848 | -+ "c2", |
3849 | -+ []*http.Cookie{ |
3850 | -+ {Name: "c2", Value: "v2"}, |
3851 | -+ }, |
3852 | -+ }, |
3853 | -+ { |
3854 | -+ http.Header{"Cookie": {"Cookie-1=v$1; c2=v2"}}, |
3855 | -+ "", |
3856 | -+ []*http.Cookie{ |
3857 | -+ {Name: "Cookie-1", Value: "v$1"}, |
3858 | -+ {Name: "c2", Value: "v2"}, |
3859 | -+ }, |
3860 | -+ }, |
3861 | -+ { |
3862 | -+ http.Header{"Cookie": {"Cookie-1=v$1; c2=v2"}}, |
3863 | -+ "c2", |
3864 | -+ []*http.Cookie{ |
3865 | - {Name: "c2", Value: "v2"}, |
3866 | - }, |
3867 | - }, |
3868 | +@@ -957,7 +890,7 @@ |
3869 | + |
3870 | + func TestClientTrailers(t *testing.T) { |
3871 | + defer afterTest(t) |
3872 | +- ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { |
3873 | ++ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
3874 | + w.Header().Set("Connection", "close") |
3875 | + w.Header().Set("Trailer", "Server-Trailer-A, Server-Trailer-B") |
3876 | + w.Header().Add("Trailer", "Server-Trailer-C") |
3877 | +@@ -992,9 +925,9 @@ |
3878 | + // trailers to be sent, if and only if they were |
3879 | + // previously declared with w.Header().Set("Trailer", |
3880 | + // ..keys..) |
3881 | +- w.(Flusher).Flush() |
3882 | +- conn, buf, _ := w.(Hijacker).Hijack() |
3883 | +- t := Header{} |
3884 | ++ w.(http.Flusher).Flush() |
3885 | ++ conn, buf, _ := w.(http.Hijacker).Hijack() |
3886 | ++ t := http.Header{} |
3887 | + t.Set("Server-Trailer-A", "valuea") |
3888 | + t.Set("Server-Trailer-C", "valuec") // skipping B |
3889 | + buf.WriteString("0\r\n") // eof |
3890 | +@@ -1015,7 +948,7 @@ |
3891 | + req.Trailer["Client-Trailer-B"] = []string{"valueb"} |
3892 | + }), |
3893 | + )) |
3894 | +- req.Trailer = Header{ |
3895 | ++ req.Trailer = http.Header{ |
3896 | + "Client-Trailer-A": nil, // to be set later |
3897 | + "Client-Trailer-B": nil, // to be set later |
3898 | + } |
3899 | +@@ -1027,7 +960,7 @@ |
3900 | + if err := wantBody(res, err, "decl: [Client-Trailer-A Client-Trailer-B], vals: valuea, valueb"); err != nil { |
3901 | + t.Error(err) |
3902 | + } |
3903 | +- want := Header{ |
3904 | ++ want := http.Header{ |
3905 | + "Server-Trailer-A": []string{"valuea"}, |
3906 | + "Server-Trailer-B": nil, |
3907 | + "Server-Trailer-C": []string{"valuec"}, |
3908 | |
3909 | === modified file 'http13client/export_test.go' |
3910 | ---- http13client/export_test.go 2014-03-19 20:20:19 +0000 |
3911 | -+++ http13client/export_test.go 2014-03-19 22:27:37 +0000 |
3912 | +--- http13client/export_test.go 2014-06-20 11:00:47 +0000 |
3913 | ++++ http13client/export_test.go 2014-06-20 12:00:22 +0000 |
3914 | @@ -9,15 +9,12 @@ |
3915 | |
3916 | import ( |
3917 | @@ -599,8 +416,8 @@ |
3918 | noProxyEnv.reset() |
3919 | |
3920 | === modified file 'http13client/header_test.go' |
3921 | ---- http13client/header_test.go 2014-03-19 20:20:19 +0000 |
3922 | -+++ http13client/header_test.go 2014-03-19 22:27:37 +0000 |
3923 | +--- http13client/header_test.go 2014-06-20 11:00:47 +0000 |
3924 | ++++ http13client/header_test.go 2014-06-20 12:00:22 +0000 |
3925 | @@ -6,19 +6,20 @@ |
3926 | |
3927 | import ( |
3928 | @@ -731,8 +548,8 @@ |
3929 | } |
3930 | |
3931 | === modified file 'http13client/npn_test.go' |
3932 | ---- http13client/npn_test.go 2014-03-19 21:38:56 +0000 |
3933 | -+++ http13client/npn_test.go 2014-03-19 22:27:37 +0000 |
3934 | +--- http13client/npn_test.go 2014-06-20 11:00:47 +0000 |
3935 | ++++ http13client/npn_test.go 2014-06-20 12:05:53 +0000 |
3936 | @@ -11,13 +11,14 @@ |
3937 | "io" |
3938 | "io/ioutil" |
3939 | @@ -792,8 +609,8 @@ |
3940 | func (w http09Writer) WriteHeader(int) {} // no headers |
3941 | |
3942 | === modified file 'http13client/readrequest_test.go' |
3943 | ---- http13client/readrequest_test.go 2014-03-19 20:20:19 +0000 |
3944 | -+++ http13client/readrequest_test.go 2014-03-19 22:27:37 +0000 |
3945 | +--- http13client/readrequest_test.go 2014-06-20 11:00:47 +0000 |
3946 | ++++ http13client/readrequest_test.go 2014-06-20 12:00:22 +0000 |
3947 | @@ -9,6 +9,7 @@ |
3948 | "bytes" |
3949 | "fmt" |
3950 | @@ -909,8 +726,8 @@ |
3951 | Close: false, |
3952 | |
3953 | === modified file 'http13client/request_test.go' |
3954 | ---- http13client/request_test.go 2014-03-19 21:38:56 +0000 |
3955 | -+++ http13client/request_test.go 2014-03-19 22:27:37 +0000 |
3956 | +--- http13client/request_test.go 2014-06-20 11:00:47 +0000 |
3957 | ++++ http13client/request_test.go 2014-06-20 12:05:53 +0000 |
3958 | @@ -12,6 +12,7 @@ |
3959 | "io/ioutil" |
3960 | "mime/multipart" |
3961 | @@ -945,8 +762,26 @@ |
3962 | + req.Header = http.Header{"Content-Type": {"text/plain"}} |
3963 | multipart, err = req.MultipartReader() |
3964 | if multipart != nil { |
3965 | - t.Errorf("unexpected multipart for text/plain") |
3966 | -@@ -159,7 +160,7 @@ |
3967 | + t.Error("unexpected multipart for text/plain") |
3968 | +@@ -161,7 +162,7 @@ |
3969 | + func TestParseMultipartForm(t *testing.T) { |
3970 | + req := &Request{ |
3971 | + Method: "POST", |
3972 | +- Header: Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}}, |
3973 | ++ Header: http.Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}}, |
3974 | + Body: ioutil.NopCloser(new(bytes.Buffer)), |
3975 | + } |
3976 | + err := req.ParseMultipartForm(25) |
3977 | +@@ -169,7 +170,7 @@ |
3978 | + t.Error("expected multipart EOF, got nil") |
3979 | + } |
3980 | + |
3981 | +- req.Header = Header{"Content-Type": {"text/plain"}} |
3982 | ++ req.Header = http.Header{"Content-Type": {"text/plain"}} |
3983 | + err = req.ParseMultipartForm(25) |
3984 | + if err != ErrNotMultipart { |
3985 | + t.Error("expected ErrNotMultipart for text/plain") |
3986 | +@@ -177,7 +178,7 @@ |
3987 | } |
3988 | |
3989 | func TestRedirect(t *testing.T) { |
3990 | @@ -957,8 +792,8 @@ |
3991 | w.Header().Set("Location", "/foo/") |
3992 | |
3993 | === modified file 'http13client/requestwrite_test.go' |
3994 | ---- http13client/requestwrite_test.go 2014-03-19 20:20:19 +0000 |
3995 | -+++ http13client/requestwrite_test.go 2014-03-19 22:27:37 +0000 |
3996 | +--- http13client/requestwrite_test.go 2014-06-20 11:00:47 +0000 |
3997 | ++++ http13client/requestwrite_test.go 2014-06-20 12:00:22 +0000 |
3998 | @@ -10,6 +10,7 @@ |
3999 | "fmt" |
4000 | "io" |
4001 | @@ -1068,8 +903,8 @@ |
4002 | var braw bytes.Buffer |
4003 | |
4004 | === modified file 'http13client/response_test.go' |
4005 | ---- http13client/response_test.go 2014-03-19 20:20:19 +0000 |
4006 | -+++ http13client/response_test.go 2014-03-19 22:27:37 +0000 |
4007 | +--- http13client/response_test.go 2014-06-20 11:00:47 +0000 |
4008 | ++++ http13client/response_test.go 2014-06-20 12:05:53 +0000 |
4009 | @@ -12,6 +12,7 @@ |
4010 | "fmt" |
4011 | "io" |
4012 | @@ -1078,7 +913,7 @@ |
4013 | "net/url" |
4014 | "reflect" |
4015 | "regexp" |
4016 | -@@ -44,7 +45,7 @@ |
4017 | +@@ -48,7 +49,7 @@ |
4018 | ProtoMajor: 1, |
4019 | ProtoMinor: 0, |
4020 | Request: dummyReq("GET"), |
4021 | @@ -1087,7 +922,7 @@ |
4022 | "Connection": {"close"}, // TODO(rsc): Delete? |
4023 | }, |
4024 | Close: true, |
4025 | -@@ -67,7 +68,7 @@ |
4026 | +@@ -71,7 +72,7 @@ |
4027 | Proto: "HTTP/1.1", |
4028 | ProtoMajor: 1, |
4029 | ProtoMinor: 1, |
4030 | @@ -1096,7 +931,7 @@ |
4031 | Request: dummyReq("GET"), |
4032 | Close: true, |
4033 | ContentLength: -1, |
4034 | -@@ -88,7 +89,7 @@ |
4035 | +@@ -92,7 +93,7 @@ |
4036 | Proto: "HTTP/1.1", |
4037 | ProtoMajor: 1, |
4038 | ProtoMinor: 1, |
4039 | @@ -1105,7 +940,7 @@ |
4040 | Request: dummyReq("GET"), |
4041 | Close: false, |
4042 | ContentLength: 0, |
4043 | -@@ -112,7 +113,7 @@ |
4044 | +@@ -116,7 +117,7 @@ |
4045 | ProtoMajor: 1, |
4046 | ProtoMinor: 0, |
4047 | Request: dummyReq("GET"), |
4048 | @@ -1114,61 +949,61 @@ |
4049 | "Connection": {"close"}, |
4050 | "Content-Length": {"10"}, |
4051 | }, |
4052 | -@@ -142,7 +143,7 @@ |
4053 | - ProtoMajor: 1, |
4054 | - ProtoMinor: 1, |
4055 | - Request: dummyReq("GET"), |
4056 | -- Header: Header{}, |
4057 | -+ Header: http.Header{}, |
4058 | - Close: false, |
4059 | - ContentLength: -1, |
4060 | - TransferEncoding: []string{"chunked"}, |
4061 | -@@ -169,7 +170,7 @@ |
4062 | - ProtoMajor: 1, |
4063 | - ProtoMinor: 1, |
4064 | - Request: dummyReq("GET"), |
4065 | -- Header: Header{}, |
4066 | -+ Header: http.Header{}, |
4067 | - Close: false, |
4068 | - ContentLength: -1, |
4069 | - TransferEncoding: []string{"chunked"}, |
4070 | -@@ -191,7 +192,7 @@ |
4071 | - ProtoMajor: 1, |
4072 | - ProtoMinor: 1, |
4073 | - Request: dummyReq("HEAD"), |
4074 | -- Header: Header{}, |
4075 | -+ Header: http.Header{}, |
4076 | - TransferEncoding: []string{"chunked"}, |
4077 | - Close: false, |
4078 | - ContentLength: -1, |
4079 | -@@ -213,7 +214,7 @@ |
4080 | - ProtoMajor: 1, |
4081 | - ProtoMinor: 0, |
4082 | - Request: dummyReq("HEAD"), |
4083 | -- Header: Header{"Content-Length": {"256"}}, |
4084 | -+ Header: http.Header{"Content-Length": {"256"}}, |
4085 | - TransferEncoding: nil, |
4086 | - Close: true, |
4087 | - ContentLength: 256, |
4088 | -@@ -235,7 +236,7 @@ |
4089 | - ProtoMajor: 1, |
4090 | - ProtoMinor: 1, |
4091 | - Request: dummyReq("HEAD"), |
4092 | -- Header: Header{"Content-Length": {"256"}}, |
4093 | -+ Header: http.Header{"Content-Length": {"256"}}, |
4094 | - TransferEncoding: nil, |
4095 | - Close: false, |
4096 | - ContentLength: 256, |
4097 | -@@ -256,7 +257,7 @@ |
4098 | - ProtoMajor: 1, |
4099 | - ProtoMinor: 0, |
4100 | - Request: dummyReq("HEAD"), |
4101 | -- Header: Header{}, |
4102 | -+ Header: http.Header{}, |
4103 | - TransferEncoding: nil, |
4104 | - Close: true, |
4105 | - ContentLength: -1, |
4106 | -@@ -278,7 +279,7 @@ |
4107 | +@@ -146,7 +147,7 @@ |
4108 | + ProtoMajor: 1, |
4109 | + ProtoMinor: 1, |
4110 | + Request: dummyReq("GET"), |
4111 | +- Header: Header{}, |
4112 | ++ Header: http.Header{}, |
4113 | + Close: false, |
4114 | + ContentLength: -1, |
4115 | + TransferEncoding: []string{"chunked"}, |
4116 | +@@ -173,7 +174,7 @@ |
4117 | + ProtoMajor: 1, |
4118 | + ProtoMinor: 1, |
4119 | + Request: dummyReq("GET"), |
4120 | +- Header: Header{}, |
4121 | ++ Header: http.Header{}, |
4122 | + Close: false, |
4123 | + ContentLength: -1, |
4124 | + TransferEncoding: []string{"chunked"}, |
4125 | +@@ -195,7 +196,7 @@ |
4126 | + ProtoMajor: 1, |
4127 | + ProtoMinor: 1, |
4128 | + Request: dummyReq("HEAD"), |
4129 | +- Header: Header{}, |
4130 | ++ Header: http.Header{}, |
4131 | + TransferEncoding: []string{"chunked"}, |
4132 | + Close: false, |
4133 | + ContentLength: -1, |
4134 | +@@ -217,7 +218,7 @@ |
4135 | + ProtoMajor: 1, |
4136 | + ProtoMinor: 0, |
4137 | + Request: dummyReq("HEAD"), |
4138 | +- Header: Header{"Content-Length": {"256"}}, |
4139 | ++ Header: http.Header{"Content-Length": {"256"}}, |
4140 | + TransferEncoding: nil, |
4141 | + Close: true, |
4142 | + ContentLength: 256, |
4143 | +@@ -239,7 +240,7 @@ |
4144 | + ProtoMajor: 1, |
4145 | + ProtoMinor: 1, |
4146 | + Request: dummyReq("HEAD"), |
4147 | +- Header: Header{"Content-Length": {"256"}}, |
4148 | ++ Header: http.Header{"Content-Length": {"256"}}, |
4149 | + TransferEncoding: nil, |
4150 | + Close: false, |
4151 | + ContentLength: 256, |
4152 | +@@ -260,7 +261,7 @@ |
4153 | + ProtoMajor: 1, |
4154 | + ProtoMinor: 0, |
4155 | + Request: dummyReq("HEAD"), |
4156 | +- Header: Header{}, |
4157 | ++ Header: http.Header{}, |
4158 | + TransferEncoding: nil, |
4159 | + Close: true, |
4160 | + ContentLength: -1, |
4161 | +@@ -282,7 +283,7 @@ |
4162 | ProtoMajor: 1, |
4163 | ProtoMinor: 1, |
4164 | Request: dummyReq("GET"), |
4165 | @@ -1177,25 +1012,25 @@ |
4166 | "Content-Length": {"0"}, |
4167 | }, |
4168 | Close: false, |
4169 | -@@ -299,7 +300,7 @@ |
4170 | - ProtoMajor: 1, |
4171 | - ProtoMinor: 0, |
4172 | - Request: dummyReq("GET"), |
4173 | -- Header: Header{}, |
4174 | -+ Header: http.Header{}, |
4175 | - Close: true, |
4176 | - ContentLength: -1, |
4177 | - }, |
4178 | -@@ -318,7 +319,7 @@ |
4179 | - ProtoMajor: 1, |
4180 | - ProtoMinor: 0, |
4181 | - Request: dummyReq("GET"), |
4182 | -- Header: Header{}, |
4183 | -+ Header: http.Header{}, |
4184 | - Close: true, |
4185 | - ContentLength: -1, |
4186 | - }, |
4187 | -@@ -340,7 +341,7 @@ |
4188 | +@@ -303,7 +304,7 @@ |
4189 | + ProtoMajor: 1, |
4190 | + ProtoMinor: 0, |
4191 | + Request: dummyReq("GET"), |
4192 | +- Header: Header{}, |
4193 | ++ Header: http.Header{}, |
4194 | + Close: true, |
4195 | + ContentLength: -1, |
4196 | + }, |
4197 | +@@ -322,7 +323,7 @@ |
4198 | + ProtoMajor: 1, |
4199 | + ProtoMinor: 0, |
4200 | + Request: dummyReq("GET"), |
4201 | +- Header: Header{}, |
4202 | ++ Header: http.Header{}, |
4203 | + Close: true, |
4204 | + ContentLength: -1, |
4205 | + }, |
4206 | +@@ -344,7 +345,7 @@ |
4207 | ProtoMajor: 1, |
4208 | ProtoMinor: 1, |
4209 | Request: dummyReq("GET"), |
4210 | @@ -1204,7 +1039,7 @@ |
4211 | "Content-Type": []string{"multipart/byteranges; boundary=18a75608c8f47cef"}, |
4212 | }, |
4213 | Close: true, |
4214 | -@@ -363,7 +364,7 @@ |
4215 | +@@ -367,7 +368,7 @@ |
4216 | Proto: "HTTP/1.0", |
4217 | ProtoMajor: 1, |
4218 | ProtoMinor: 0, |
4219 | @@ -1213,7 +1048,7 @@ |
4220 | "Connection": {"close"}, // TODO(rsc): Delete? |
4221 | }, |
4222 | Close: true, |
4223 | -@@ -545,7 +546,7 @@ |
4224 | +@@ -549,7 +550,7 @@ |
4225 | func TestLocationResponse(t *testing.T) { |
4226 | for i, tt := range responseLocationTests { |
4227 | res := new(Response) |
4228 | @@ -1222,7 +1057,7 @@ |
4229 | res.Header.Set("Location", tt.location) |
4230 | if tt.requrl != "" { |
4231 | res.Request = &Request{} |
4232 | -@@ -626,16 +627,3 @@ |
4233 | +@@ -630,16 +631,3 @@ |
4234 | t.Errorf("ReadResponse = %v; want io.ErrUnexpectedEOF", err) |
4235 | } |
4236 | } |
4237 | @@ -1241,8 +1076,8 @@ |
4238 | -} |
4239 | |
4240 | === modified file 'http13client/responsewrite_test.go' |
4241 | ---- http13client/responsewrite_test.go 2014-03-19 20:20:19 +0000 |
4242 | -+++ http13client/responsewrite_test.go 2014-03-19 22:27:37 +0000 |
4243 | +--- http13client/responsewrite_test.go 2014-06-20 11:00:47 +0000 |
4244 | ++++ http13client/responsewrite_test.go 2014-06-20 12:05:53 +0000 |
4245 | @@ -7,6 +7,7 @@ |
4246 | import ( |
4247 | "bytes" |
4248 | @@ -1257,7 +1092,7 @@ |
4249 | Request: dummyReq("GET"), |
4250 | - Header: Header{}, |
4251 | + Header: http.Header{}, |
4252 | - Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")), |
4253 | + Body: ioutil.NopCloser(strings.NewReader("abcdef")), |
4254 | ContentLength: 6, |
4255 | }, |
4256 | @@ -41,7 +42,7 @@ |
4257 | @@ -1270,6 +1105,60 @@ |
4258 | ContentLength: -1, |
4259 | }, |
4260 | @@ -56,7 +57,7 @@ |
4261 | + ProtoMajor: 1, |
4262 | + ProtoMinor: 1, |
4263 | + Request: dummyReq("GET"), |
4264 | +- Header: Header{}, |
4265 | ++ Header: http.Header{}, |
4266 | + Body: ioutil.NopCloser(strings.NewReader("abcdef")), |
4267 | + ContentLength: -1, |
4268 | + Close: true, |
4269 | +@@ -73,7 +74,7 @@ |
4270 | + ProtoMajor: 1, |
4271 | + ProtoMinor: 1, |
4272 | + Request: dummyReq11("GET"), |
4273 | +- Header: Header{}, |
4274 | ++ Header: http.Header{}, |
4275 | + Body: ioutil.NopCloser(strings.NewReader("abcdef")), |
4276 | + ContentLength: -1, |
4277 | + Close: false, |
4278 | +@@ -91,7 +92,7 @@ |
4279 | + ProtoMajor: 1, |
4280 | + ProtoMinor: 1, |
4281 | + Request: dummyReq11("GET"), |
4282 | +- Header: Header{}, |
4283 | ++ Header: http.Header{}, |
4284 | + Body: ioutil.NopCloser(strings.NewReader("abcdef")), |
4285 | + ContentLength: -1, |
4286 | + TransferEncoding: []string{"chunked"}, |
4287 | +@@ -108,7 +109,7 @@ |
4288 | + ProtoMajor: 1, |
4289 | + ProtoMinor: 1, |
4290 | + Request: dummyReq11("GET"), |
4291 | +- Header: Header{}, |
4292 | ++ Header: http.Header{}, |
4293 | + Body: nil, |
4294 | + ContentLength: 0, |
4295 | + Close: false, |
4296 | +@@ -124,7 +125,7 @@ |
4297 | + ProtoMajor: 1, |
4298 | + ProtoMinor: 1, |
4299 | + Request: dummyReq11("GET"), |
4300 | +- Header: Header{}, |
4301 | ++ Header: http.Header{}, |
4302 | + Body: ioutil.NopCloser(strings.NewReader("")), |
4303 | + ContentLength: 0, |
4304 | + Close: false, |
4305 | +@@ -140,7 +141,7 @@ |
4306 | + ProtoMajor: 1, |
4307 | + ProtoMinor: 1, |
4308 | + Request: dummyReq11("GET"), |
4309 | +- Header: Header{}, |
4310 | ++ Header: http.Header{}, |
4311 | + Body: ioutil.NopCloser(strings.NewReader("foo")), |
4312 | + ContentLength: 0, |
4313 | + Close: false, |
4314 | +@@ -156,7 +157,7 @@ |
4315 | ProtoMajor: 1, |
4316 | ProtoMinor: 1, |
4317 | Request: dummyReq("GET"), |
4318 | @@ -1278,7 +1167,7 @@ |
4319 | Body: ioutil.NopCloser(strings.NewReader("abcdef")), |
4320 | ContentLength: 6, |
4321 | TransferEncoding: []string{"chunked"}, |
4322 | -@@ -77,7 +78,7 @@ |
4323 | +@@ -177,7 +178,7 @@ |
4324 | ProtoMajor: 1, |
4325 | ProtoMinor: 1, |
4326 | Request: dummyReq("GET"), |
4327 | @@ -1287,11 +1176,20 @@ |
4328 | "Foo": []string{" Bar\nBaz "}, |
4329 | }, |
4330 | Body: nil, |
4331 | +@@ -200,7 +201,7 @@ |
4332 | + ProtoMajor: 1, |
4333 | + ProtoMinor: 1, |
4334 | + Request: &Request{Method: "POST"}, |
4335 | +- Header: Header{}, |
4336 | ++ Header: http.Header{}, |
4337 | + ContentLength: 0, |
4338 | + TransferEncoding: nil, |
4339 | + Body: nil, |
4340 | |
4341 | === modified file 'http13client/transport_test.go' |
4342 | ---- http13client/transport_test.go 2014-03-19 21:38:56 +0000 |
4343 | -+++ http13client/transport_test.go 2014-03-19 22:27:37 +0000 |
4344 | -@@ -17,8 +17,8 @@ |
4345 | +--- http13client/transport_test.go 2014-06-20 11:00:47 +0000 |
4346 | ++++ http13client/transport_test.go 2014-06-20 12:05:53 +0000 |
4347 | +@@ -18,8 +18,8 @@ |
4348 | "io/ioutil" |
4349 | "log" |
4350 | "net" |
4351 | @@ -1301,7 +1199,7 @@ |
4352 | "net/http/httptest" |
4353 | "net/url" |
4354 | "os" |
4355 | -@@ -34,7 +34,7 @@ |
4356 | +@@ -35,7 +35,7 @@ |
4357 | // and then verify that the final 2 responses get errors back. |
4358 | |
4359 | // hostPortHandler writes back the client's "host:port". |
4360 | @@ -1310,7 +1208,7 @@ |
4361 | if r.FormValue("close") == "true" { |
4362 | w.Header().Set("Connection", "close") |
4363 | } |
4364 | -@@ -280,7 +280,7 @@ |
4365 | +@@ -289,7 +289,7 @@ |
4366 | const msg = "foobar" |
4367 | |
4368 | var addrSeen map[string]int |
4369 | @@ -1319,7 +1217,7 @@ |
4370 | addrSeen[r.RemoteAddr]++ |
4371 | if r.URL.Path == "/chunked/" { |
4372 | w.WriteHeader(200) |
4373 | -@@ -299,7 +299,7 @@ |
4374 | +@@ -308,7 +308,7 @@ |
4375 | wantLen := []int{len(msg), -1}[pi] |
4376 | addrSeen = make(map[string]int) |
4377 | for i := 0; i < 3; i++ { |
4378 | @@ -1328,7 +1226,7 @@ |
4379 | if err != nil { |
4380 | t.Errorf("Get %s: %v", path, err) |
4381 | continue |
4382 | -@@ -329,7 +329,7 @@ |
4383 | +@@ -338,7 +338,7 @@ |
4384 | defer afterTest(t) |
4385 | resch := make(chan string) |
4386 | gotReq := make(chan bool) |
4387 | @@ -1337,7 +1235,7 @@ |
4388 | gotReq <- true |
4389 | msg := <-resch |
4390 | _, err := w.Write([]byte(msg)) |
4391 | -@@ -457,12 +457,12 @@ |
4392 | +@@ -466,12 +466,12 @@ |
4393 | if testing.Short() { |
4394 | t.Skip("skipping test in short mode") |
4395 | } |
4396 | @@ -1353,7 +1251,7 @@ |
4397 | buf.Flush() |
4398 | conn.Close() |
4399 | })) |
4400 | -@@ -510,7 +510,7 @@ |
4401 | +@@ -519,7 +519,7 @@ |
4402 | // with no bodies properly |
4403 | func TestTransportHeadResponses(t *testing.T) { |
4404 | defer afterTest(t) |
4405 | @@ -1362,7 +1260,7 @@ |
4406 | if r.Method != "HEAD" { |
4407 | panic("expected HEAD; got " + r.Method) |
4408 | } |
4409 | -@@ -545,7 +545,7 @@ |
4410 | +@@ -554,7 +554,7 @@ |
4411 | // on responses to HEAD requests. |
4412 | func TestTransportHeadChunkedResponse(t *testing.T) { |
4413 | defer afterTest(t) |
4414 | @@ -1371,7 +1269,7 @@ |
4415 | if r.Method != "HEAD" { |
4416 | panic("expected HEAD; got " + r.Method) |
4417 | } |
4418 | -@@ -588,7 +588,7 @@ |
4419 | +@@ -597,7 +597,7 @@ |
4420 | func TestRoundTripGzip(t *testing.T) { |
4421 | defer afterTest(t) |
4422 | const responseBody = "test response body" |
4423 | @@ -1380,7 +1278,7 @@ |
4424 | accept := req.Header.Get("Accept-Encoding") |
4425 | if expect := req.FormValue("expect_accept"); accept != expect { |
4426 | t.Errorf("in handler, test %v: Accept-Encoding = %q, want %q", |
4427 | -@@ -647,7 +647,7 @@ |
4428 | +@@ -656,7 +656,7 @@ |
4429 | defer afterTest(t) |
4430 | const testString = "The test string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" |
4431 | const nRandBytes = 1024 * 1024 |
4432 | @@ -1389,7 +1287,7 @@ |
4433 | if req.Method == "HEAD" { |
4434 | if g := req.Header.Get("Accept-Encoding"); g != "" { |
4435 | t.Errorf("HEAD request sent with Accept-Encoding of %q; want none", g) |
4436 | -@@ -742,11 +742,11 @@ |
4437 | +@@ -751,11 +751,11 @@ |
4438 | func TestTransportProxy(t *testing.T) { |
4439 | defer afterTest(t) |
4440 | ch := make(chan string, 1) |
4441 | @@ -1403,7 +1301,7 @@ |
4442 | ch <- "proxy for " + r.URL.String() |
4443 | })) |
4444 | defer proxy.Close() |
4445 | -@@ -770,7 +770,7 @@ |
4446 | +@@ -779,7 +779,7 @@ |
4447 | // Content-Encoding is removed. |
4448 | func TestTransportGzipRecursive(t *testing.T) { |
4449 | defer afterTest(t) |
4450 | @@ -1412,7 +1310,16 @@ |
4451 | w.Header().Set("Content-Encoding", "gzip") |
4452 | w.Write(rgz) |
4453 | })) |
4454 | -@@ -802,7 +802,7 @@ |
4455 | +@@ -807,7 +807,7 @@ |
4456 | + // a short gzip body |
4457 | + func TestTransportGzipShort(t *testing.T) { |
4458 | + defer afterTest(t) |
4459 | +- ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { |
4460 | ++ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
4461 | + w.Header().Set("Content-Encoding", "gzip") |
4462 | + w.Write([]byte{0x1f, 0x8b}) |
4463 | + })) |
4464 | +@@ -838,7 +838,7 @@ |
4465 | defer afterTest(t) |
4466 | gotReqCh := make(chan bool) |
4467 | unblockCh := make(chan bool) |
4468 | @@ -1421,7 +1328,7 @@ |
4469 | gotReqCh <- true |
4470 | <-unblockCh |
4471 | w.Header().Set("Content-Length", "0") |
4472 | -@@ -869,7 +869,7 @@ |
4473 | +@@ -905,7 +905,7 @@ |
4474 | t.Skip("skipping test; see http://golang.org/issue/7237") |
4475 | } |
4476 | defer afterTest(t) |
4477 | @@ -1430,7 +1337,7 @@ |
4478 | })) |
4479 | defer ts.Close() |
4480 | |
4481 | -@@ -912,7 +912,7 @@ |
4482 | +@@ -948,7 +948,7 @@ |
4483 | c := &Client{Transport: tr} |
4484 | |
4485 | unblockCh := make(chan bool, 1) |
4486 | @@ -1439,7 +1346,7 @@ |
4487 | <-unblockCh |
4488 | tr.CloseIdleConnections() |
4489 | })) |
4490 | -@@ -939,7 +939,7 @@ |
4491 | +@@ -975,7 +975,7 @@ |
4492 | func TestIssue3644(t *testing.T) { |
4493 | defer afterTest(t) |
4494 | const numFoos = 5000 |
4495 | @@ -1448,7 +1355,7 @@ |
4496 | w.Header().Set("Connection", "close") |
4497 | for i := 0; i < numFoos; i++ { |
4498 | w.Write([]byte("foo ")) |
4499 | -@@ -967,8 +967,8 @@ |
4500 | +@@ -1003,8 +1003,8 @@ |
4501 | func TestIssue3595(t *testing.T) { |
4502 | defer afterTest(t) |
4503 | const deniedMsg = "sorry, denied." |
4504 | @@ -1459,7 +1366,7 @@ |
4505 | })) |
4506 | defer ts.Close() |
4507 | tr := &Transport{} |
4508 | -@@ -991,7 +991,7 @@ |
4509 | +@@ -1027,7 +1027,7 @@ |
4510 | // "client fails to handle requests with no body and chunked encoding" |
4511 | func TestChunkedNoContent(t *testing.T) { |
4512 | defer afterTest(t) |
4513 | @@ -1468,7 +1375,7 @@ |
4514 | w.WriteHeader(StatusNoContent) |
4515 | })) |
4516 | defer ts.Close() |
4517 | -@@ -1019,7 +1019,7 @@ |
4518 | +@@ -1055,7 +1055,7 @@ |
4519 | maxProcs, numReqs = 4, 50 |
4520 | } |
4521 | defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs)) |
4522 | @@ -1477,7 +1384,7 @@ |
4523 | fmt.Fprintf(w, "%v", r.FormValue("echo")) |
4524 | })) |
4525 | defer ts.Close() |
4526 | -@@ -1080,8 +1080,8 @@ |
4527 | +@@ -1116,8 +1116,8 @@ |
4528 | } |
4529 | defer afterTest(t) |
4530 | const debug = false |
4531 | @@ -1488,7 +1395,7 @@ |
4532 | io.Copy(w, neverEnding('a')) |
4533 | }) |
4534 | ts := httptest.NewServer(mux) |
4535 | -@@ -1144,11 +1144,11 @@ |
4536 | +@@ -1180,11 +1180,11 @@ |
4537 | } |
4538 | defer afterTest(t) |
4539 | const debug = false |
4540 | @@ -1503,20 +1410,22 @@ |
4541 | defer r.Body.Close() |
4542 | io.Copy(ioutil.Discard, r.Body) |
4543 | }) |
4544 | -@@ -1214,9 +1214,9 @@ |
4545 | - if testing.Short() { |
4546 | +@@ -1251,11 +1251,11 @@ |
4547 | t.Skip("skipping timeout test in -short mode") |
4548 | } |
4549 | + inHandler := make(chan bool, 1) |
4550 | - mux := NewServeMux() |
4551 | -- mux.HandleFunc("/fast", func(w ResponseWriter, r *Request) {}) |
4552 | +- mux.HandleFunc("/fast", func(w ResponseWriter, r *Request) { |
4553 | ++ mux := http.NewServeMux() |
4554 | ++ mux.HandleFunc("/fast", func(w http.ResponseWriter, r *http.Request) { |
4555 | + inHandler <- true |
4556 | + }) |
4557 | - mux.HandleFunc("/slow", func(w ResponseWriter, r *Request) { |
4558 | -+ mux := http.NewServeMux() |
4559 | -+ mux.HandleFunc("/fast", func(w http.ResponseWriter, r *http.Request) {}) |
4560 | + mux.HandleFunc("/slow", func(w http.ResponseWriter, r *http.Request) { |
4561 | + inHandler <- true |
4562 | time.Sleep(2 * time.Second) |
4563 | }) |
4564 | - ts := httptest.NewServer(mux) |
4565 | -@@ -1276,9 +1276,9 @@ |
4566 | +@@ -1322,9 +1322,9 @@ |
4567 | t.Skip("skipping test in -short mode") |
4568 | } |
4569 | unblockc := make(chan bool) |
4570 | @@ -1528,7 +1437,7 @@ |
4571 | <-unblockc |
4572 | })) |
4573 | defer ts.Close() |
4574 | -@@ -1386,14 +1386,14 @@ |
4575 | +@@ -1431,14 +1431,14 @@ |
4576 | defer afterTest(t) |
4577 | writeErr := make(chan error, 1) |
4578 | msg := []byte("young\n") |
4579 | @@ -1545,7 +1454,7 @@ |
4580 | } |
4581 | })) |
4582 | defer ts.Close() |
4583 | -@@ -1449,7 +1449,7 @@ |
4584 | +@@ -1494,7 +1494,7 @@ |
4585 | res := &Response{ |
4586 | Status: "200 OK", |
4587 | StatusCode: 200, |
4588 | @@ -1554,7 +1463,7 @@ |
4589 | Body: ioutil.NopCloser(strings.NewReader("You wanted " + req.URL.String())), |
4590 | } |
4591 | return res, nil |
4592 | -@@ -1478,7 +1478,7 @@ |
4593 | +@@ -1523,7 +1523,7 @@ |
4594 | defer afterTest(t) |
4595 | tr := &Transport{} |
4596 | _, err := tr.RoundTrip(&Request{ |
4597 | @@ -1563,7 +1472,7 @@ |
4598 | URL: &url.URL{ |
4599 | Scheme: "http", |
4600 | }, |
4601 | -@@ -1492,14 +1492,14 @@ |
4602 | +@@ -1537,14 +1537,14 @@ |
4603 | func TestTransportSocketLateBinding(t *testing.T) { |
4604 | defer afterTest(t) |
4605 | |
4606 | @@ -1582,7 +1491,7 @@ |
4607 | w.Header().Set("bar-ipport", r.RemoteAddr) |
4608 | }) |
4609 | ts := httptest.NewServer(mux) |
4610 | -@@ -1720,7 +1720,7 @@ |
4611 | +@@ -1767,7 +1767,7 @@ |
4612 | var mu sync.Mutex |
4613 | var n int |
4614 | |
4615 | @@ -1591,7 +1500,7 @@ |
4616 | mu.Lock() |
4617 | n++ |
4618 | mu.Unlock() |
4619 | -@@ -1756,7 +1756,7 @@ |
4620 | +@@ -1803,7 +1803,7 @@ |
4621 | // then closes it. |
4622 | func TestTransportClosesRequestBody(t *testing.T) { |
4623 | defer afterTest(t) |
4624 | @@ -1600,4 +1509,49 @@ |
4625 | io.Copy(ioutil.Discard, r.Body) |
4626 | })) |
4627 | defer ts.Close() |
4628 | +@@ -1890,9 +1890,9 @@ |
4629 | + t.Skip("skipping flaky test on Windows; golang.org/issue/7634") |
4630 | + } |
4631 | + closedc := make(chan bool, 1) |
4632 | +- ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) { |
4633 | ++ ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
4634 | + if strings.Contains(r.URL.Path, "/keep-alive-then-die") { |
4635 | +- conn, _, _ := w.(Hijacker).Hijack() |
4636 | ++ conn, _, _ := w.(http.Hijacker).Hijack() |
4637 | + conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nfoo")) |
4638 | + conn.Close() |
4639 | + closedc <- true |
4640 | +@@ -1994,12 +1994,12 @@ |
4641 | + } |
4642 | + defer closeConn() |
4643 | + |
4644 | +- ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { |
4645 | ++ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
4646 | + if r.Method == "GET" { |
4647 | + io.WriteString(w, "bar") |
4648 | + return |
4649 | + } |
4650 | +- conn, _, _ := w.(Hijacker).Hijack() |
4651 | ++ conn, _, _ := w.(http.Hijacker).Hijack() |
4652 | + sconn.Lock() |
4653 | + sconn.c = conn |
4654 | + sconn.Unlock() |
4655 | +@@ -2056,7 +2056,7 @@ |
4656 | + } |
4657 | + defer afterTest(t) |
4658 | + readBody := make(chan error, 1) |
4659 | +- ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { |
4660 | ++ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
4661 | + _, err := ioutil.ReadAll(r.Body) |
4662 | + readBody <- err |
4663 | + })) |
4664 | +@@ -2098,7 +2098,7 @@ |
4665 | + } |
4666 | + } |
4667 | + |
4668 | +-func wantBody(res *http.Response, err error, want string) error { |
4669 | ++func wantBody(res *Response, err error, want string) error { |
4670 | + if err != nil { |
4671 | + return err |
4672 | + } |
4673 | |
4674 | |
4675 | === modified file 'http13client/_using.txt' |
4676 | --- http13client/_using.txt 2014-03-20 12:20:01 +0000 |
4677 | +++ http13client/_using.txt 2014-07-02 13:12:36 +0000 |
4678 | @@ -1,5 +1,5 @@ |
4679 | -parent: 19512:32c32aef2a41 tip |
4680 | - test: enable bug385_32 test on amd64p32. |
4681 | -branch: default |
4682 | +parent: 20169:9895f9e36435 go1.3 release |
4683 | + go1.3 |
4684 | +branch: release-branch.go1.3 |
4685 | commit: (clean) |
4686 | update: (current) |
4687 | |
4688 | === modified file 'http13client/client.go' |
4689 | --- http13client/client.go 2014-03-20 09:26:28 +0000 |
4690 | +++ http13client/client.go 2014-07-02 13:12:36 +0000 |
4691 | @@ -92,8 +92,9 @@ |
4692 | // authentication, or cookies. |
4693 | // |
4694 | // RoundTrip should not modify the request, except for |
4695 | - // consuming and closing the Body. The request's URL and |
4696 | - // Header fields are guaranteed to be initialized. |
4697 | + // consuming and closing the Body, including on errors. The |
4698 | + // request's URL and Header fields are guaranteed to be |
4699 | + // initialized. |
4700 | RoundTrip(*Request) (*Response, error) |
4701 | } |
4702 | |
4703 | @@ -141,6 +142,9 @@ |
4704 | // (typically Transport) may not be able to re-use a persistent TCP |
4705 | // connection to the server for a subsequent "keep-alive" request. |
4706 | // |
4707 | +// The request Body, if non-nil, will be closed by the underlying |
4708 | +// Transport, even on errors. |
4709 | +// |
4710 | // Generally Get, Post, or PostForm will be used instead of Do. |
4711 | func (c *Client) Do(req *Request) (resp *Response, err error) { |
4712 | if req.Method == "GET" || req.Method == "HEAD" { |
4713 | @@ -163,14 +167,17 @@ |
4714 | // Caller should close resp.Body when done reading from it. |
4715 | func send(req *Request, t RoundTripper) (resp *Response, err error) { |
4716 | if t == nil { |
4717 | + req.closeBody() |
4718 | return nil, errors.New("http: no Client.Transport or DefaultTransport") |
4719 | } |
4720 | |
4721 | if req.URL == nil { |
4722 | + req.closeBody() |
4723 | return nil, errors.New("http: nil Request.URL") |
4724 | } |
4725 | |
4726 | if req.RequestURI != "" { |
4727 | + req.closeBody() |
4728 | return nil, errors.New("http: Request.RequestURI can't be set in client requests.") |
4729 | } |
4730 | |
4731 | @@ -278,6 +285,7 @@ |
4732 | var via []*Request |
4733 | |
4734 | if ireq.URL == nil { |
4735 | + ireq.closeBody() |
4736 | return nil, errors.New("http: nil Request.URL") |
4737 | } |
4738 | |
4739 | @@ -400,7 +408,7 @@ |
4740 | // Caller should close resp.Body when done reading from it. |
4741 | // |
4742 | // If the provided body is also an io.Closer, it is closed after the |
4743 | -// body is successfully written to the server. |
4744 | +// request. |
4745 | func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) { |
4746 | req, err := NewRequest("POST", url, body) |
4747 | if err != nil { |
4748 | |
4749 | === modified file 'http13client/client_test.go' |
4750 | --- http13client/client_test.go 2014-03-20 09:26:28 +0000 |
4751 | +++ http13client/client_test.go 2014-07-02 13:12:36 +0000 |
4752 | @@ -20,6 +20,8 @@ |
4753 | "net/http" |
4754 | "net/http/httptest" |
4755 | "net/url" |
4756 | + "reflect" |
4757 | + "sort" |
4758 | "strconv" |
4759 | "strings" |
4760 | "sync" |
4761 | @@ -877,3 +879,93 @@ |
4762 | t.Fatal("server saw different client ports before & after the redirect") |
4763 | } |
4764 | } |
4765 | + |
4766 | +// eofReaderFunc is an io.Reader that runs itself, and then returns io.EOF. |
4767 | +type eofReaderFunc func() |
4768 | + |
4769 | +func (f eofReaderFunc) Read(p []byte) (n int, err error) { |
4770 | + f() |
4771 | + return 0, io.EOF |
4772 | +} |
4773 | + |
4774 | +func TestClientTrailers(t *testing.T) { |
4775 | + defer afterTest(t) |
4776 | + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
4777 | + w.Header().Set("Connection", "close") |
4778 | + w.Header().Set("Trailer", "Server-Trailer-A, Server-Trailer-B") |
4779 | + w.Header().Add("Trailer", "Server-Trailer-C") |
4780 | + |
4781 | + var decl []string |
4782 | + for k := range r.Trailer { |
4783 | + decl = append(decl, k) |
4784 | + } |
4785 | + sort.Strings(decl) |
4786 | + |
4787 | + slurp, err := ioutil.ReadAll(r.Body) |
4788 | + if err != nil { |
4789 | + t.Errorf("Server reading request body: %v", err) |
4790 | + } |
4791 | + if string(slurp) != "foo" { |
4792 | + t.Errorf("Server read request body %q; want foo", slurp) |
4793 | + } |
4794 | + if r.Trailer == nil { |
4795 | + io.WriteString(w, "nil Trailer") |
4796 | + } else { |
4797 | + fmt.Fprintf(w, "decl: %v, vals: %s, %s", |
4798 | + decl, |
4799 | + r.Trailer.Get("Client-Trailer-A"), |
4800 | + r.Trailer.Get("Client-Trailer-B")) |
4801 | + } |
4802 | + |
4803 | + // TODO: golang.org/issue/7759: there's no way yet for |
4804 | + // the server to set trailers without hijacking, so do |
4805 | + // that for now, just to test the client. Later, in |
4806 | + // Go 1.4, it should be implicit that any mutations |
4807 | + // to w.Header() after the initial write are the |
4808 | + // trailers to be sent, if and only if they were |
4809 | + // previously declared with w.Header().Set("Trailer", |
4810 | + // ..keys..) |
4811 | + w.(http.Flusher).Flush() |
4812 | + conn, buf, _ := w.(http.Hijacker).Hijack() |
4813 | + t := http.Header{} |
4814 | + t.Set("Server-Trailer-A", "valuea") |
4815 | + t.Set("Server-Trailer-C", "valuec") // skipping B |
4816 | + buf.WriteString("0\r\n") // eof |
4817 | + t.Write(buf) |
4818 | + buf.WriteString("\r\n") // end of trailers |
4819 | + buf.Flush() |
4820 | + conn.Close() |
4821 | + })) |
4822 | + defer ts.Close() |
4823 | + |
4824 | + var req *Request |
4825 | + req, _ = NewRequest("POST", ts.URL, io.MultiReader( |
4826 | + eofReaderFunc(func() { |
4827 | + req.Trailer["Client-Trailer-A"] = []string{"valuea"} |
4828 | + }), |
4829 | + strings.NewReader("foo"), |
4830 | + eofReaderFunc(func() { |
4831 | + req.Trailer["Client-Trailer-B"] = []string{"valueb"} |
4832 | + }), |
4833 | + )) |
4834 | + req.Trailer = http.Header{ |
4835 | + "Client-Trailer-A": nil, // to be set later |
4836 | + "Client-Trailer-B": nil, // to be set later |
4837 | + } |
4838 | + req.ContentLength = -1 |
4839 | + res, err := DefaultClient.Do(req) |
4840 | + if err != nil { |
4841 | + t.Fatal(err) |
4842 | + } |
4843 | + if err := wantBody(res, err, "decl: [Client-Trailer-A Client-Trailer-B], vals: valuea, valueb"); err != nil { |
4844 | + t.Error(err) |
4845 | + } |
4846 | + want := http.Header{ |
4847 | + "Server-Trailer-A": []string{"valuea"}, |
4848 | + "Server-Trailer-B": nil, |
4849 | + "Server-Trailer-C": []string{"valuec"}, |
4850 | + } |
4851 | + if !reflect.DeepEqual(res.Trailer, want) { |
4852 | + t.Errorf("Response trailers = %#v; want %#v", res.Trailer, want) |
4853 | + } |
4854 | +} |
4855 | |
4856 | === modified file 'http13client/cookie.go' |
4857 | --- http13client/cookie.go 2014-03-19 23:13:58 +0000 |
4858 | +++ http13client/cookie.go 2014-07-02 13:12:36 +0000 |
4859 | @@ -55,11 +55,7 @@ |
4860 | attr, val = attr[:j], attr[j+1:] |
4861 | } |
4862 | lowerAttr := strings.ToLower(attr) |
4863 | - parseCookieValueFn := parseCookieValue |
4864 | - if lowerAttr == "expires" { |
4865 | - parseCookieValueFn = parseCookieExpiresValue |
4866 | - } |
4867 | - val, success = parseCookieValueFn(val) |
4868 | + val, success = parseCookieValue(val) |
4869 | if !success { |
4870 | c.Unparsed = append(c.Unparsed, parts[i]) |
4871 | continue |
4872 | @@ -230,12 +226,23 @@ |
4873 | // ; US-ASCII characters excluding CTLs, |
4874 | // ; whitespace DQUOTE, comma, semicolon, |
4875 | // ; and backslash |
4876 | +// We loosen this as spaces and commas are common in cookie values |
4877 | +// but we produce a quoted cookie-value in when value starts or ends |
4878 | +// with a comma or space. |
4879 | +// See http://golang.org/issue/7243 for the discussion. |
4880 | func sanitizeCookieValue(v string) string { |
4881 | - return sanitizeOrWarn("Cookie.Value", validCookieValueByte, v) |
4882 | + v = sanitizeOrWarn("Cookie.Value", validCookieValueByte, v) |
4883 | + if len(v) == 0 { |
4884 | + return v |
4885 | + } |
4886 | + if v[0] == ' ' || v[0] == ',' || v[len(v)-1] == ' ' || v[len(v)-1] == ',' { |
4887 | + return `"` + v + `"` |
4888 | + } |
4889 | + return v |
4890 | } |
4891 | |
4892 | func validCookieValueByte(b byte) bool { |
4893 | - return 0x20 < b && b < 0x7f && b != '"' && b != ',' && b != ';' && b != '\\' |
4894 | + return 0x20 <= b && b < 0x7f && b != '"' && b != ';' && b != '\\' |
4895 | } |
4896 | |
4897 | // path-av = "Path=" path-value |
4898 | @@ -270,38 +277,13 @@ |
4899 | return string(buf) |
4900 | } |
4901 | |
4902 | -func unquoteCookieValue(v string) string { |
4903 | - if len(v) > 1 && v[0] == '"' && v[len(v)-1] == '"' { |
4904 | - return v[1 : len(v)-1] |
4905 | - } |
4906 | - return v |
4907 | -} |
4908 | - |
4909 | -func isCookieByte(c byte) bool { |
4910 | - switch { |
4911 | - case c == 0x21, 0x23 <= c && c <= 0x2b, 0x2d <= c && c <= 0x3a, |
4912 | - 0x3c <= c && c <= 0x5b, 0x5d <= c && c <= 0x7e: |
4913 | - return true |
4914 | - } |
4915 | - return false |
4916 | -} |
4917 | - |
4918 | -func isCookieExpiresByte(c byte) (ok bool) { |
4919 | - return isCookieByte(c) || c == ',' || c == ' ' |
4920 | -} |
4921 | - |
4922 | func parseCookieValue(raw string) (string, bool) { |
4923 | - return parseCookieValueUsing(raw, isCookieByte) |
4924 | -} |
4925 | - |
4926 | -func parseCookieExpiresValue(raw string) (string, bool) { |
4927 | - return parseCookieValueUsing(raw, isCookieExpiresByte) |
4928 | -} |
4929 | - |
4930 | -func parseCookieValueUsing(raw string, validByte func(byte) bool) (string, bool) { |
4931 | - raw = unquoteCookieValue(raw) |
4932 | + // Strip the quotes, if present. |
4933 | + if len(raw) > 1 && raw[0] == '"' && raw[len(raw)-1] == '"' { |
4934 | + raw = raw[1 : len(raw)-1] |
4935 | + } |
4936 | for i := 0; i < len(raw); i++ { |
4937 | - if !validByte(raw[i]) { |
4938 | + if !validCookieValueByte(raw[i]) { |
4939 | return "", false |
4940 | } |
4941 | } |
4942 | |
4943 | === removed file 'http13client/cookie_test.go' |
4944 | --- http13client/cookie_test.go 2014-03-19 23:13:58 +0000 |
4945 | +++ http13client/cookie_test.go 1970-01-01 00:00:00 +0000 |
4946 | @@ -1,304 +0,0 @@ |
4947 | -// Copyright 2010 The Go Authors. All rights reserved. |
4948 | -// Use of this source code is governed by a BSD-style |
4949 | -// license that can be found in the LICENSE file. |
4950 | - |
4951 | -package http |
4952 | - |
4953 | -import ( |
4954 | - "bytes" |
4955 | - "encoding/json" |
4956 | - "fmt" |
4957 | - "log" |
4958 | - "net/http" |
4959 | - "os" |
4960 | - "reflect" |
4961 | - "strings" |
4962 | - "testing" |
4963 | - "time" |
4964 | -) |
4965 | - |
4966 | -var writeSetCookiesTests = []struct { |
4967 | - Cookie *http.Cookie |
4968 | - Raw string |
4969 | -}{ |
4970 | - { |
4971 | - &http.Cookie{Name: "cookie-1", Value: "v$1"}, |
4972 | - "cookie-1=v$1", |
4973 | - }, |
4974 | - { |
4975 | - &http.Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600}, |
4976 | - "cookie-2=two; Max-Age=3600", |
4977 | - }, |
4978 | - { |
4979 | - &http.Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"}, |
4980 | - "cookie-3=three; Domain=example.com", |
4981 | - }, |
4982 | - { |
4983 | - &http.Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"}, |
4984 | - "cookie-4=four; Path=/restricted/", |
4985 | - }, |
4986 | - { |
4987 | - &http.Cookie{Name: "cookie-5", Value: "five", Domain: "wrong;bad.abc"}, |
4988 | - "cookie-5=five", |
4989 | - }, |
4990 | - { |
4991 | - &http.Cookie{Name: "cookie-6", Value: "six", Domain: "bad-.abc"}, |
4992 | - "cookie-6=six", |
4993 | - }, |
4994 | - { |
4995 | - &http.Cookie{Name: "cookie-7", Value: "seven", Domain: "127.0.0.1"}, |
4996 | - "cookie-7=seven; Domain=127.0.0.1", |
4997 | - }, |
4998 | - { |
4999 | - &http.Cookie{Name: "cookie-8", Value: "eight", Domain: "::1"}, |
5000 | - "cookie-8=eight", |