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