Merge lp:~chipaca/ubuntu-push/the-merge-automatic into lp:ubuntu-push

Proposed by John Lenton
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
Reviewer Review Type Date Requested Status
Ubuntu Push Hackers Pending
Review via email: mp+224289@code.launchpad.net

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

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Makefile'
--- Makefile 2014-06-02 11:37:03 +0000
+++ Makefile 2014-07-02 13:12:36 +0000
@@ -36,8 +36,8 @@
36 $(GOPATH)/bin/godeps -u dependencies.tsv36 $(GOPATH)/bin/godeps -u dependencies.tsv
37 go install $(GODEPS)37 go install $(GODEPS)
3838
39dependencies.tsv: $(TOBUILD)39dependencies.tsv:
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 > $@
4141
42check:42check:
43 go test $(TESTFLAGS) $(TOTEST)43 go test $(TESTFLAGS) $(TOTEST)
4444
=== modified file 'PACKAGE_DEPS'
--- PACKAGE_DEPS 2014-05-23 06:21:46 +0000
+++ PACKAGE_DEPS 2014-07-02 13:12:36 +0000
@@ -8,3 +8,5 @@
8libsqlite3-dev8libsqlite3-dev
9libubuntuoneauth-2.0-dev9libubuntuoneauth-2.0-dev
10libwhoopsie-dev10libwhoopsie-dev
11libmessaging-menu-dev
12libubuntu-app-launch2-dev
1113
=== modified file 'bus/connectivity/connectivity_test.go'
--- bus/connectivity/connectivity_test.go 2014-05-09 10:48:39 +0000
+++ bus/connectivity/connectivity_test.go 2014-07-02 13:12:36 +0000
@@ -160,13 +160,13 @@
160 return nil160 return nil
161}161}
162162
163func (*racyEndpoint) Close() {}163func (*racyEndpoint) Close() {}
164func (*racyEndpoint) Dial() error { return nil }164func (*racyEndpoint) Dial() error { return nil }
165func (*racyEndpoint) String() string { return "racyEndpoint" }165func (*racyEndpoint) String() string { return "racyEndpoint" }
166func (*racyEndpoint) Call(string, []interface{}, ...interface{}) error { return nil }166func (*racyEndpoint) Call(string, []interface{}, ...interface{}) error { return nil }
167func (*racyEndpoint) GrabName(bool) <-chan error { return nil }167func (*racyEndpoint) GrabName(bool) <-chan error { return nil }
168func (*racyEndpoint) WatchMethod(bus.DispatchMap, ...interface{}) {}168func (*racyEndpoint) WatchMethod(bus.DispatchMap, string, ...interface{}) {}
169func (*racyEndpoint) Signal(member string, args []interface{}) error { return nil }169func (*racyEndpoint) Signal(member string, suffix string, args []interface{}) error { return nil }
170170
171var _ bus.Endpoint = (*racyEndpoint)(nil)171var _ bus.Endpoint = (*racyEndpoint)(nil)
172172
173173
=== modified file 'bus/endpoint.go'
--- bus/endpoint.go 2014-05-15 11:28:06 +0000
+++ bus/endpoint.go 2014-07-02 13:12:36 +0000
@@ -21,7 +21,9 @@
21import (21import (
22 "errors"22 "errors"
23 "fmt"23 "fmt"
24
24 "launchpad.net/go-dbus/v1"25 "launchpad.net/go-dbus/v1"
26
25 "launchpad.net/ubuntu-push/logger"27 "launchpad.net/ubuntu-push/logger"
26)28)
2729
@@ -29,15 +31,15 @@
29 * Endpoint (and its implementation)31 * Endpoint (and its implementation)
30 */32 */
3133
32type BusMethod func([]interface{}, []interface{}) ([]interface{}, error)34type BusMethod func(string, []interface{}, []interface{}) ([]interface{}, error)
33type DispatchMap map[string]BusMethod35type DispatchMap map[string]BusMethod
3436
35// bus.Endpoint represents the DBus connection itself.37// bus.Endpoint represents the DBus connection itself.
36type Endpoint interface {38type Endpoint interface {
37 GrabName(allowReplacement bool) <-chan error39 GrabName(allowReplacement bool) <-chan error
38 WatchSignal(member string, f func(...interface{}), d func()) error40 WatchSignal(member string, f func(...interface{}), d func()) error
39 WatchMethod(DispatchMap, ...interface{})41 WatchMethod(DispatchMap, string, ...interface{})
40 Signal(string, []interface{}) error42 Signal(string, string, []interface{}) error
41 Call(member string, args []interface{}, rvs ...interface{}) error43 Call(member string, args []interface{}, rvs ...interface{}) error
42 GetProperty(property string) (interface{}, error)44 GetProperty(property string) (interface{}, error)
43 Dial() error45 Dial() error
@@ -212,8 +214,9 @@
212// Signal() sends out a signal called <member> containing <args>.214// Signal() sends out a signal called <member> containing <args>.
213//215//
214// XXX: untested216// XXX: untested
215func (endp *endpoint) Signal(member string, args []interface{}) error {217func (endp *endpoint) Signal(member string, suffix string, args []interface{}) error {
216 msg := dbus.NewSignalMessage(dbus.ObjectPath(endp.addr.Path), endp.addr.Interface, member)218 path := dbus.ObjectPath(endp.addr.Path + suffix)
219 msg := dbus.NewSignalMessage(path, endp.addr.Interface, member)
217 if args != nil {220 if args != nil {
218 err := msg.AppendArgs(args...)221 err := msg.AppendArgs(args...)
219 if err != nil {222 if err != nil {
@@ -234,7 +237,7 @@
234// calls.237// calls.
235//238//
236// XXX: untested239// XXX: untested
237func (endp *endpoint) WatchMethod(dispatch DispatchMap, extra ...interface{}) {240func (endp *endpoint) WatchMethod(dispatch DispatchMap, suffix string, extra ...interface{}) {
238 ch := make(chan *dbus.Message)241 ch := make(chan *dbus.Message)
239 go func() {242 go func() {
240 var reply *dbus.Message243 var reply *dbus.Message
@@ -249,12 +252,12 @@
249 endp.log.Errorf("WatchMethod: unknown method %s", msg.Member)252 endp.log.Errorf("WatchMethod: unknown method %s", msg.Member)
250 } else {253 } else {
251 args := msg.AllArgs()254 args := msg.AllArgs()
252 rvals, err := meth(args, extra)255 rvals, err := meth(string(msg.Path), args, extra)
253 if err != nil {256 if err != nil {
254 reply = dbus.NewErrorMessage(msg, err_iface, err.Error())257 reply = dbus.NewErrorMessage(msg, err_iface, err.Error())
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)
256 } else {259 } else {
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)
258 reply = dbus.NewMethodReturnMessage(msg)261 reply = dbus.NewMethodReturnMessage(msg)
259 err = reply.AppendArgs(rvals...)262 err = reply.AppendArgs(rvals...)
260 if err != nil {263 if err != nil {
@@ -270,7 +273,8 @@
270273
271 }274 }
272 }()275 }()
273 endp.bus.RegisterObjectPath(dbus.ObjectPath(endp.addr.Path), ch)276 path := dbus.ObjectPath(endp.addr.Path + suffix)
277 endp.bus.RegisterObjectPath(path, ch)
274}278}
275279
276/*280/*
277281
=== added directory 'bus/notifications/app_helper'
=== added file 'bus/notifications/app_helper/app_helper_c.go'
--- bus/notifications/app_helper/app_helper_c.go 1970-01-01 00:00:00 +0000
+++ bus/notifications/app_helper/app_helper_c.go 2014-07-02 13:12:36 +0000
@@ -0,0 +1,41 @@
1/*
2 Copyright 2013-2014 Canonical Ltd.
3
4 This program is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License version 3, as published
6 by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranties of
10 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17// Package app_helper wraps C functions to access app information
18package app_helper
19
20/*
21#cgo pkg-config: gio-unix-2.0
22#cgo pkg-config: gio-2.0
23#include <stdlib.h>
24#include <glib.h>
25#include <gio/gdesktopappinfo.h>
26*/
27import "C"
28import "unsafe"
29
30func AppIconFromId(appId string) string {
31 _id := C.CString(appId)
32 defer C.free(unsafe.Pointer(_id))
33 _app_info := C.g_desktop_app_info_new(_id)
34 defer C.g_app_info_delete(_app_info)
35 _app_icon := C.g_app_info_get_icon(_app_info)
36 defer C.g_object_unref((C.gpointer)(_app_icon))
37 _icon_string := C.g_icon_to_string(_app_icon)
38 defer C.free(unsafe.Pointer(_icon_string))
39 name := C.GoString((*C.char)(_icon_string))
40 return name
41}
042
=== modified file 'bus/notifications/raw.go'
--- bus/notifications/raw.go 2014-05-20 10:50:04 +0000
+++ bus/notifications/raw.go 2014-07-02 13:12:36 +0000
@@ -23,9 +23,12 @@
2323
24import (24import (
25 "errors"25 "errors"
26 "fmt"
2627
27 "launchpad.net/go-dbus/v1"28 "launchpad.net/go-dbus/v1"
28 "launchpad.net/ubuntu-push/bus"29 "launchpad.net/ubuntu-push/bus"
30 c_helper "launchpad.net/ubuntu-push/bus/notifications/app_helper"
31 "launchpad.net/ubuntu-push/launch_helper"
29 "launchpad.net/ubuntu-push/logger"32 "launchpad.net/ubuntu-push/logger"
30)33)
3134
@@ -96,3 +99,36 @@
96 }99 }
97 return ch, nil100 return ch, nil
98}101}
102
103// ShowCard displays a given card.
104//
105// If card.Actions has 1 action, it's an interactive notification.
106// If card.Actions has 2 or more actions, it will show as a snap decision.
107//
108// WatchActions will receive something like this in the ActionId field:
109// appId::notificationId::action.Id
110func (raw *RawNotifications) Present(appId string, notificationId string, notification *launch_helper.Notification) (uint32, error) {
111 if notification == nil || notification.Card == nil || !notification.Card.Popup || notification.Card.Summary == "" {
112 raw.log.Debugf("skipping notification: nil, or nil card, or not popup, or no summary: %#v", notification)
113 return 0, nil
114 }
115
116 card := notification.Card
117
118 app_icon := c_helper.AppIconFromId(appId)
119 hints := make(map[string]*dbus.Variant)
120 hints["x-canonical-secondary-icon"] = &dbus.Variant{app_icon}
121
122 actions := make([]string, 2*len(card.Actions))
123 for i, action := range card.Actions {
124 actions[2*i] = fmt.Sprintf("%s::%s::%d", appId, notificationId, i)
125 actions[2*i+1] = action
126 }
127 switch len(actions) {
128 case 2:
129 hints["x-canonical-switch-to-application"] = &dbus.Variant{true}
130 case 4:
131 hints["x-canonical-snap-decisions"] = &dbus.Variant{true}
132 }
133 return raw.Notify(appId, 0, card.Icon, card.Summary, card.Body, actions, hints, 30*1000)
134}
99135
=== modified file 'bus/notifications/raw_test.go'
--- bus/notifications/raw_test.go 2014-05-20 10:50:04 +0000
+++ bus/notifications/raw_test.go 2014-07-02 13:12:36 +0000
@@ -22,6 +22,7 @@
22import (22import (
23 . "launchpad.net/gocheck"23 . "launchpad.net/gocheck"
24 testibus "launchpad.net/ubuntu-push/bus/testing"24 testibus "launchpad.net/ubuntu-push/bus/testing"
25 "launchpad.net/ubuntu-push/launch_helper"
25 "launchpad.net/ubuntu-push/logger"26 "launchpad.net/ubuntu-push/logger"
26 helpers "launchpad.net/ubuntu-push/testing"27 helpers "launchpad.net/ubuntu-push/testing"
27 "launchpad.net/ubuntu-push/testing/condition"28 "launchpad.net/ubuntu-push/testing/condition"
@@ -95,3 +96,45 @@
95 _, err := raw.WatchActions()96 _, err := raw.WatchActions()
96 c.Check(err, NotNil)97 c.Check(err, NotNil)
97}98}
99
100func (s *RawSuite) TestPresentNotifies(c *C) {
101 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
102 raw := Raw(endp, s.log)
103 nid, err := raw.Present("firefox.desktop", "notifId", &launch_helper.Notification{Card: &launch_helper.Card{Summary: "summary", Popup: true}})
104 c.Check(err, IsNil)
105 c.Check(nid, Equals, uint32(1))
106}
107
108func (s *RawSuite) TestPresentNoNotificationDoesNotNotify(c *C) {
109 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
110 raw := Raw(endp, s.log)
111 nid, err := raw.Present("firefox.desktop", "notifId", nil)
112 c.Check(err, IsNil)
113 c.Check(nid, Equals, uint32(0))
114}
115
116func (s *RawSuite) TestPresentNoCardDoesNotNotify(c *C) {
117 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
118 raw := Raw(endp, s.log)
119 nid, err := raw.Present("firefox.desktop", "notifId", &launch_helper.Notification{})
120 c.Check(err, IsNil)
121 c.Check(nid, Equals, uint32(0))
122}
123
124func (s *RawSuite) TestPresentNoSummaryDoesNotNotify(c *C) {
125 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
126 raw := Raw(endp, s.log)
127 nid, err := raw.Present("firefox.desktop", "notifId", &launch_helper.Notification{Card: &launch_helper.Card{}})
128 c.Check(err, IsNil)
129 c.Check(nid, Equals, uint32(0))
130}
131
132func (s *RawSuite) TestPresentNoPopupNoNotify(c *C) {
133 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
134 raw := Raw(endp, s.log)
135 nid, err := raw.Present("firefox.desktop", "notifId", &launch_helper.Notification{Card: &launch_helper.Card{Summary: "summary"}})
136 c.Check(err, IsNil)
137 c.Check(nid, Equals, uint32(0))
138}
139
140// XXX Missing test about ShowCard manipulating Actions and hints correctly.
98141
=== modified file 'bus/testing/testing_endpoint.go'
--- bus/testing/testing_endpoint.go 2014-05-15 12:27:32 +0000
+++ bus/testing/testing_endpoint.go 2014-07-02 13:12:36 +0000
@@ -187,7 +187,7 @@
187 return nil187 return nil
188}188}
189189
190func (tc *testingEndpoint) WatchMethod(dispatch bus.DispatchMap, extra ...interface{}) {190func (tc *testingEndpoint) WatchMethod(dispatch bus.DispatchMap, suffix string, extra ...interface{}) {
191 tc.callArgsLck.Lock()191 tc.callArgsLck.Lock()
192 defer tc.callArgsLck.Unlock()192 defer tc.callArgsLck.Unlock()
193193
@@ -196,12 +196,12 @@
196 tc.callArgs = append(tc.callArgs, args)196 tc.callArgs = append(tc.callArgs, args)
197}197}
198198
199func (tc *testingEndpoint) Signal(member string, args []interface{}) error {199func (tc *testingEndpoint) Signal(member string, suffix string, args []interface{}) error {
200 tc.callArgsLck.Lock()200 tc.callArgsLck.Lock()
201 defer tc.callArgsLck.Unlock()201 defer tc.callArgsLck.Unlock()
202202
203 callargs := callArgs{Member: "::Signal"}203 callargs := callArgs{Member: "::Signal"}
204 callargs.Args = append(callargs.Args, member, args)204 callargs.Args = append(callargs.Args, member, suffix, args)
205 tc.callArgs = append(tc.callArgs, callargs)205 tc.callArgs = append(tc.callArgs, callargs)
206206
207 return nil207 return nil
208208
=== modified file 'bus/testing/testing_endpoint_test.go'
--- bus/testing/testing_endpoint_test.go 2014-06-02 10:04:34 +0000
+++ bus/testing/testing_endpoint_test.go 2014-07-02 13:12:36 +0000
@@ -214,24 +214,24 @@
214// Test that Signal updates callArgs214// Test that Signal updates callArgs
215func (s *TestingEndpointSuite) TestSignalUpdatesCallArgs(c *C) {215func (s *TestingEndpointSuite) TestSignalUpdatesCallArgs(c *C) {
216 endp := NewTestingEndpoint(nil, condition.Work(true))216 endp := NewTestingEndpoint(nil, condition.Work(true))
217 endp.Signal("hello", []interface{}{"world"})217 endp.Signal("hello", "", []interface{}{"world"})
218 endp.Signal("hello", []interface{}{"there"})218 endp.Signal("hello", "/potato", []interface{}{"there"})
219 c.Check(GetCallArgs(endp), DeepEquals, []callArgs{219 c.Check(GetCallArgs(endp), DeepEquals, []callArgs{
220 {220 {
221 Member: "::Signal",221 Member: "::Signal",
222 Args: []interface{}{"hello", []interface{}{"world"}},222 Args: []interface{}{"hello", "", []interface{}{"world"}},
223 }, {223 }, {
224 Member: "::Signal",224 Member: "::Signal",
225 Args: []interface{}{"hello", []interface{}{"there"}},225 Args: []interface{}{"hello", "/potato", []interface{}{"there"}},
226 }})226 }})
227}227}
228228
229// Test that WatchMethod updates callArgs229// Test that WatchMethod updates callArgs
230func (s *TestingEndpointSuite) TestWatchMethodUpdatesCallArgs(c *C) {230func (s *TestingEndpointSuite) TestWatchMethodUpdatesCallArgs(c *C) {
231 endp := NewTestingEndpoint(nil, condition.Work(true))231 endp := NewTestingEndpoint(nil, condition.Work(true))
232 foo := func([]interface{}, []interface{}) ([]interface{}, error) { return nil, nil }232 foo := func(string, []interface{}, []interface{}) ([]interface{}, error) { return nil, nil }
233 foomp := bus.DispatchMap{"foo": foo}233 foomp := bus.DispatchMap{"foo": foo}
234 endp.WatchMethod(foomp)234 endp.WatchMethod(foomp, "/*")
235 c.Check(GetCallArgs(endp), DeepEquals, []callArgs{235 c.Check(GetCallArgs(endp), DeepEquals, []callArgs{
236 {236 {
237 Member: "::WatchMethod",237 Member: "::WatchMethod",
238238
=== modified file 'client/client.go'
--- client/client.go 2014-05-21 10:03:18 +0000
+++ client/client.go 2014-07-02 13:12:36 +0000
@@ -22,16 +22,14 @@
22 "crypto/sha256"22 "crypto/sha256"
23 "encoding/base64"23 "encoding/base64"
24 "encoding/hex"24 "encoding/hex"
25 "encoding/json"
26 "encoding/pem"25 "encoding/pem"
27 "errors"26 "errors"
28 "fmt"27 "fmt"
29 "io/ioutil"28 "io/ioutil"
30 "os"29 "os"
30 "os/exec"
31 "strings"31 "strings"
3232
33 "launchpad.net/go-dbus/v1"
34
35 "launchpad.net/ubuntu-push/bus"33 "launchpad.net/ubuntu-push/bus"
36 "launchpad.net/ubuntu-push/bus/connectivity"34 "launchpad.net/ubuntu-push/bus/connectivity"
37 "launchpad.net/ubuntu-push/bus/networkmanager"35 "launchpad.net/ubuntu-push/bus/networkmanager"
@@ -63,40 +61,38 @@
63 // The PEM-encoded server certificate61 // The PEM-encoded server certificate
64 CertPEMFile string `json:"cert_pem_file"`62 CertPEMFile string `json:"cert_pem_file"`
65 // How to invoke the auth helper63 // How to invoke the auth helper
66 AuthHelper []string `json:"auth_helper"`64 AuthHelper string `json:"auth_helper"`
65 SessionURL string `json:"session_url"`
66 RegistrationURL string `json:"registration_url"`
67 // The logging level (one of "debug", "info", "error")67 // The logging level (one of "debug", "info", "error")
68 LogLevel logger.ConfigLogLevel `json:"log_level"`68 LogLevel logger.ConfigLogLevel `json:"log_level"`
69}69}
7070
71// PushClient is the Ubuntu Push Notifications client-side daemon.71// PushClient is the Ubuntu Push Notifications client-side daemon.
72type PushClient struct {72type PushClient struct {
73 leveldbPath string73 leveldbPath string
74 configPath string74 configPath string
75 config ClientConfig75 config ClientConfig
76 log logger.Logger76 log logger.Logger
77 pem []byte77 pem []byte
78 idder identifier.Id78 idder identifier.Id
79 deviceId string79 deviceId string
80 notificationsEndp bus.Endpoint80 notificationsEndp bus.Endpoint
81 urlDispatcherEndp bus.Endpoint81 urlDispatcherEndp bus.Endpoint
82 connectivityEndp bus.Endpoint82 connectivityEndp bus.Endpoint
83 systemImageEndp bus.Endpoint83 systemImageEndp bus.Endpoint
84 systemImageInfo *systemimage.InfoResult84 systemImageInfo *systemimage.InfoResult
85 connCh chan bool85 connCh chan bool
86 hasConnectivity bool86 hasConnectivity bool
87 actionsCh <-chan notifications.RawActionReply87 actionsCh <-chan notifications.RawActionReply
88 session *session.ClientSession88 session *session.ClientSession
89 sessionConnectedCh chan uint3289 sessionConnectedCh chan uint32
90 serviceEndpoint bus.Endpoint90 pushServiceEndpoint bus.Endpoint
91 service *service.Service91 pushService *service.PushService
92 postalServiceEndpoint bus.Endpoint
93 postalService *service.PostalService
92}94}
9395
94var (
95 system_update_url = "settings:///system/system-update"
96 ACTION_ID_SNOWFLAKE = "::ubuntu-push-client::"
97 ACTION_ID_BROADCAST = ACTION_ID_SNOWFLAKE + system_update_url
98)
99
100// Creates a new Ubuntu Push Notifications client-side daemon that will use96// Creates a new Ubuntu Push Notifications client-side daemon that will use
101// the given configuration file.97// the given configuration file.
102func NewPushClient(configPath string, leveldbPath string) *PushClient {98func NewPushClient(configPath string, leveldbPath string) *PushClient {
@@ -128,10 +124,12 @@
128124
129 // overridden for testing125 // overridden for testing
130 client.idder = identifier.New()126 client.idder = identifier.New()
131 client.notificationsEndp = bus.SessionBus.Endpoint(notifications.BusAddress, client.log)
132 client.urlDispatcherEndp = bus.SessionBus.Endpoint(urldispatcher.BusAddress, client.log)127 client.urlDispatcherEndp = bus.SessionBus.Endpoint(urldispatcher.BusAddress, client.log)
133 client.connectivityEndp = bus.SystemBus.Endpoint(networkmanager.BusAddress, client.log)128 client.connectivityEndp = bus.SystemBus.Endpoint(networkmanager.BusAddress, client.log)
134 client.systemImageEndp = bus.SystemBus.Endpoint(systemimage.BusAddress, client.log)129 client.systemImageEndp = bus.SystemBus.Endpoint(systemimage.BusAddress, client.log)
130 if client.notificationsEndp == nil {
131 client.notificationsEndp = bus.SessionBus.Endpoint(notifications.BusAddress, client.log)
132 }
135133
136 client.connCh = make(chan bool, 1)134 client.connCh = make(chan bool, 1)
137 client.sessionConnectedCh = make(chan uint32, 1)135 client.sessionConnectedCh = make(chan uint32, 1)
@@ -160,7 +158,27 @@
160 ExpectAllRepairedTime: client.config.ExpectAllRepairedTime.TimeDuration(),158 ExpectAllRepairedTime: client.config.ExpectAllRepairedTime.TimeDuration(),
161 PEM: client.pem,159 PEM: client.pem,
162 Info: info,160 Info: info,
163 AuthHelper: client.config.AuthHelper,161 AuthGetter: client.getAuthorization,
162 AuthURL: client.config.SessionURL,
163 }
164}
165
166// getAuthorization gets the authorization blob to send to the server
167func (client *PushClient) getAuthorization(url string) string {
168 client.log.Debugf("getting authorization for %s", url)
169 // using a helper, for now at least
170 if len(client.config.AuthHelper) == 0 {
171 // do nothing if helper is unset or empty
172 return ""
173 }
174
175 auth, err := exec.Command(client.config.AuthHelper, url).Output()
176 if err != nil {
177 // For now we just log the error, as we don't want to block unauthorized users
178 client.log.Errorf("unable to get the authorization token from the account: %v", err)
179 return ""
180 } else {
181 return strings.TrimSpace(string(auth))
164 }182 }
165}183}
166184
@@ -185,12 +203,10 @@
185 go connectivity.ConnectedState(client.connectivityEndp,203 go connectivity.ConnectedState(client.connectivityEndp,
186 client.config.ConnectivityConfig, client.log, client.connCh)204 client.config.ConnectivityConfig, client.log, client.connCh)
187 iniCh := make(chan uint32)205 iniCh := make(chan uint32)
188 go func() { iniCh <- util.NewAutoRedialer(client.notificationsEndp).Redial() }()
189 go func() { iniCh <- util.NewAutoRedialer(client.urlDispatcherEndp).Redial() }()206 go func() { iniCh <- util.NewAutoRedialer(client.urlDispatcherEndp).Redial() }()
190 go func() { iniCh <- util.NewAutoRedialer(client.systemImageEndp).Redial() }()207 go func() { iniCh <- util.NewAutoRedialer(client.systemImageEndp).Redial() }()
191 <-iniCh208 <-iniCh
192 <-iniCh209 <-iniCh
193 <-iniCh
194210
195 sysimg := systemimage.New(client.systemImageEndp, client.log)211 sysimg := systemimage.New(client.systemImageEndp, client.log)
196 info, err := sysimg.Info()212 info, err := sysimg.Info()
@@ -198,8 +214,11 @@
198 return err214 return err
199 }215 }
200 client.systemImageInfo = info216 client.systemImageInfo = info
217 return err
218}
201219
202 actionsCh, err := notifications.Raw(client.notificationsEndp, client.log).WatchActions()220func (client *PushClient) takePostalServiceBus() error {
221 actionsCh, err := client.postalService.TakeTheBus()
203 client.actionsCh = actionsCh222 client.actionsCh = actionsCh
204 return err223 return err
205}224}
@@ -296,28 +315,12 @@
296 return false315 return false
297}316}
298317
299func (client *PushClient) sendNotification(action_id, icon, summary, body string) (uint32, error) {
300 a := []string{action_id, "Switch to app"} // action value not visible on the phone
301 h := map[string]*dbus.Variant{"x-canonical-switch-to-application": &dbus.Variant{true}}
302 nots := notifications.Raw(client.notificationsEndp, client.log)
303 return nots.Notify(
304 "ubuntu-push-client", // app name
305 uint32(0), // id
306 icon, // icon
307 summary, // summary
308 body, // body
309 a, // actions
310 h, // hints
311 int32(10*1000), // timeout (ms)
312 )
313}
314
315// handleBroadcastNotification deals with receiving a broadcast notification318// handleBroadcastNotification deals with receiving a broadcast notification
316func (client *PushClient) handleBroadcastNotification(msg *session.BroadcastNotification) error {319func (client *PushClient) handleBroadcastNotification(msg *session.BroadcastNotification) error {
317 if !client.filterBroadcastNotification(msg) {320 if !client.filterBroadcastNotification(msg) {
318 return nil321 return nil
319 }322 }
320 not_id, err := client.sendNotification(ACTION_ID_BROADCAST,323 not_id, err := client.postalService.SendNotification(service.ACTION_ID_BROADCAST,
321 "update_manager_icon", "There's an updated system image.",324 "update_manager_icon", "There's an updated system image.",
322 "Tap to open the system updater.")325 "Tap to open the system updater.")
323 if err != nil {326 if err != nil {
@@ -331,7 +334,7 @@
331// handleUnicastNotification deals with receiving a unicast notification334// handleUnicastNotification deals with receiving a unicast notification
332func (client *PushClient) handleUnicastNotification(msg *protocol.Notification) error {335func (client *PushClient) handleUnicastNotification(msg *protocol.Notification) error {
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)
334 return client.service.Inject(msg.AppId, string(msg.Payload))337 return client.postalService.Inject(msg.AppId, msg.MsgId, string(msg.Payload))
335}338}
336339
337// handleClick deals with the user clicking a notification340// handleClick deals with the user clicking a notification
@@ -342,7 +345,7 @@
342 //345 //
343 // From ACM's SIGPLAN publication, (September, 1982), Article346 // From ACM's SIGPLAN publication, (September, 1982), Article
344 // "Epigrams in Programming", by Alan J. Perlis of Yale University.347 // "Epigrams in Programming", by Alan J. Perlis of Yale University.
345 url := strings.TrimPrefix(action_id, ACTION_ID_SNOWFLAKE)348 url := strings.TrimPrefix(action_id, service.ACTION_ID_SNOWFLAKE)
346 if len(url) == len(action_id) || len(url) == 0 {349 if len(url) == len(action_id) || len(url) == 0 {
347 // it didn't start with the prefix350 // it didn't start with the prefix
348 return nil351 return nil
@@ -392,52 +395,49 @@
392 client.handleErr)395 client.handleErr)
393}396}
394397
395// these are the currently supported fields of a unicast message
396type UnicastMessage struct {
397 Icon string `json:"icon"`
398 Body string `json:"body"`
399 Summary string `json:"summary"`
400 URL string `json:"url"`
401 Blob json.RawMessage `json:"blob"`
402}
403
404func (client *PushClient) messageHandler(message []byte) error {
405 var umsg = new(UnicastMessage)
406 err := json.Unmarshal(message, &umsg)
407 if err != nil {
408 client.log.Errorf("unable to unmarshal message: %v", err)
409 return err
410 }
411
412 not_id, err := client.sendNotification(
413 ACTION_ID_SNOWFLAKE+umsg.URL,
414 umsg.Icon, umsg.Summary, umsg.Body)
415
416 if err != nil {
417 client.log.Errorf("showing notification: %s", err)
418 return err
419 }
420 client.log.Debugf("got notification id %d", not_id)
421 return nil
422}
423
424func (client *PushClient) startService() error {398func (client *PushClient) startService() error {
425 if client.serviceEndpoint == nil {399 if client.pushServiceEndpoint == nil {
426 client.serviceEndpoint = bus.SessionBus.Endpoint(service.BusAddress, client.log)400 client.pushServiceEndpoint = bus.SessionBus.Endpoint(service.PushServiceBusAddress, client.log)
427 }401 }
428402 if client.postalServiceEndpoint == nil {
429 client.service = service.NewService(client.serviceEndpoint, client.log)403 client.postalServiceEndpoint = bus.SessionBus.Endpoint(service.PostalServiceBusAddress, client.log)
430 client.service.SetMessageHandler(client.messageHandler)404 }
431 return client.service.Start()405
406 client.pushService = service.NewPushService(client.pushServiceEndpoint, client.log)
407 client.pushService.SetRegistrationURL(client.config.RegistrationURL)
408 client.pushService.SetAuthGetter(client.getAuthorization)
409 client.pushService.SetDeviceId(client.deviceId)
410 if err := client.pushService.Start(); err != nil {
411 return err
412 }
413 return nil
414}
415
416func (client *PushClient) setupPostalService() error {
417 if client.notificationsEndp == nil {
418 client.notificationsEndp = bus.SessionBus.Endpoint(notifications.BusAddress, client.log)
419 }
420 client.postalService = service.NewPostalService(client.postalServiceEndpoint, client.notificationsEndp, client.log)
421 return nil
422}
423
424func (client *PushClient) startPostalService() error {
425 if err := client.postalService.Start(); err != nil {
426 return err
427 }
428 return nil
432}429}
433430
434// Start calls doStart with the "real" starters431// Start calls doStart with the "real" starters
435func (client *PushClient) Start() error {432func (client *PushClient) Start() error {
436 return client.doStart(433 return client.doStart(
437 client.configure,434 client.configure,
435 client.getDeviceId,
438 client.startService,436 client.startService,
439 client.getDeviceId,437 client.setupPostalService,
438 client.startPostalService,
440 client.takeTheBus,439 client.takeTheBus,
440 client.takePostalServiceBus,
441 client.initSession,441 client.initSession,
442 )442 )
443}443}
444444
=== modified file 'client/client_test.go'
--- client/client_test.go 2014-05-20 13:56:37 +0000
+++ client/client_test.go 2014-07-02 13:12:36 +0000
@@ -37,6 +37,7 @@
37 "launchpad.net/ubuntu-push/bus/notifications"37 "launchpad.net/ubuntu-push/bus/notifications"
38 "launchpad.net/ubuntu-push/bus/systemimage"38 "launchpad.net/ubuntu-push/bus/systemimage"
39 testibus "launchpad.net/ubuntu-push/bus/testing"39 testibus "launchpad.net/ubuntu-push/bus/testing"
40 "launchpad.net/ubuntu-push/client/service"
40 "launchpad.net/ubuntu-push/client/session"41 "launchpad.net/ubuntu-push/client/session"
41 "launchpad.net/ubuntu-push/client/session/seenstate"42 "launchpad.net/ubuntu-push/client/session/seenstate"
42 "launchpad.net/ubuntu-push/config"43 "launchpad.net/ubuntu-push/config"
@@ -103,11 +104,13 @@
103 "stabilizing_timeout": "0ms",104 "stabilizing_timeout": "0ms",
104 "connectivity_check_url": "",105 "connectivity_check_url": "",
105 "connectivity_check_md5": "",106 "connectivity_check_md5": "",
106 "addr": ":0",107 "addr": ":0",
107 "cert_pem_file": pem_file,108 "cert_pem_file": pem_file,
108 "recheck_timeout": "3h",109 "recheck_timeout": "3h",
109 "auth_helper": []string{},110 "auth_helper": "",
110 "log_level": "debug",111 "session_url": "xyzzy://",
112 "registration_url": "reg://",
113 "log_level": "debug",
111 }114 }
112 for k, v := range overrides {115 for k, v := range overrides {
113 cfgMap[k] = v116 cfgMap[k] = v
@@ -257,7 +260,7 @@
257260
258func (cs *clientSuite) TestDeriveSessionConfig(c *C) {261func (cs *clientSuite) TestDeriveSessionConfig(c *C) {
259 cs.writeTestConfig(map[string]interface{}{262 cs.writeTestConfig(map[string]interface{}{
260 "auth_helper": []string{"auth", "helper"},263 "auth_helper": "auth helper",
261 })264 })
262 info := map[string]interface{}{265 info := map[string]interface{}{
263 "foo": 1,266 "foo": 1,
@@ -272,7 +275,8 @@
272 ExpectAllRepairedTime: 30 * time.Minute,275 ExpectAllRepairedTime: 30 * time.Minute,
273 PEM: cli.pem,276 PEM: cli.pem,
274 Info: info,277 Info: info,
275 AuthHelper: []string{"auth", "helper"},278 AuthGetter: func(string) string { return "" },
279 AuthURL: "xyzzy://",
276 }280 }
277 // sanity check that we are looking at all fields281 // sanity check that we are looking at all fields
278 vExpected := reflect.ValueOf(expected)282 vExpected := reflect.ValueOf(expected)
@@ -284,6 +288,11 @@
284 }288 }
285 // finally compare289 // finally compare
286 conf := cli.deriveSessionConfig(info)290 conf := cli.deriveSessionConfig(info)
291 // compare authGetter by string
292 c.Check(fmt.Sprintf("%v", conf.AuthGetter), Equals, fmt.Sprintf("%v", cli.getAuthorization))
293 // and set it to nil
294 conf.AuthGetter = nil
295 expected.AuthGetter = nil
287 c.Check(conf, DeepEquals, expected)296 c.Check(conf, DeepEquals, expected)
288}297}
289298
@@ -292,28 +301,54 @@
292******************************************************************/301******************************************************************/
293302
294func (cs *clientSuite) TestStartServiceWorks(c *C) {303func (cs *clientSuite) TestStartServiceWorks(c *C) {
304 cs.writeTestConfig(map[string]interface{}{
305 "auth_helper": helpers.ScriptAbsPath("dummyauth.sh"),
306 })
295 cli := NewPushClient(cs.configPath, cs.leveldbPath)307 cli := NewPushClient(cs.configPath, cs.leveldbPath)
308 cli.configure()
296 cli.log = cs.log309 cli.log = cs.log
297 cli.serviceEndpoint = testibus.NewTestingEndpoint(condition.Work(true), nil)310 cli.deviceId = "fake-id"
298 c.Check(cli.service, IsNil)311 cli.pushServiceEndpoint = testibus.NewTestingEndpoint(condition.Work(true), nil)
312 cli.postalServiceEndpoint = testibus.NewTestingEndpoint(condition.Work(true), nil)
313 c.Check(cli.pushService, IsNil)
299 c.Check(cli.startService(), IsNil)314 c.Check(cli.startService(), IsNil)
300 c.Assert(cli.service, NotNil)315 c.Assert(cli.pushService, NotNil)
301 c.Check(cli.service.IsRunning(), Equals, true)316 c.Check(cli.pushService.IsRunning(), Equals, true)
302 c.Check(cli.service.GetMessageHandler(), NotNil)317 c.Check(cli.pushService.GetDeviceId(), Equals, "fake-id")
303 cli.service.Stop()318 c.Check(cli.pushService.GetRegistrationAuthorization(), Equals, "hello reg://")
319 c.Assert(cli.setupPostalService(), IsNil)
320 c.Assert(cli.startPostalService(), IsNil)
321 c.Check(cli.postalService.IsRunning(), Equals, true)
322 cli.pushService.Stop()
323 cli.postalService.Stop()
304}324}
305325
306func (cs *clientSuite) TestStartServiceErrorsOnNilLog(c *C) {326func (cs *clientSuite) TestStartServiceErrorsOnNilLog(c *C) {
307 cli := NewPushClient(cs.configPath, cs.leveldbPath)327 cli := NewPushClient(cs.configPath, cs.leveldbPath)
308 c.Check(cli.log, IsNil)328 c.Check(cli.log, IsNil)
309 c.Check(cli.startService(), NotNil)329 c.Check(cli.startService(), NotNil)
330 c.Assert(cli.setupPostalService(), IsNil)
331 c.Assert(cli.startPostalService(), NotNil)
310}332}
311333
312func (cs *clientSuite) TestStartServiceErrorsOnBusDialFail(c *C) {334func (cs *clientSuite) TestStartServiceErrorsOnBusDialPushFail(c *C) {
313 cli := NewPushClient(cs.configPath, cs.leveldbPath)335 cli := NewPushClient(cs.configPath, cs.leveldbPath)
314 cli.log = cs.log336 cli.log = cs.log
315 cli.serviceEndpoint = testibus.NewTestingEndpoint(condition.Work(false), nil)337 cli.pushServiceEndpoint = testibus.NewTestingEndpoint(condition.Work(false), nil)
338 cli.postalServiceEndpoint = testibus.NewTestingEndpoint(condition.Work(false), nil)
316 c.Check(cli.startService(), NotNil)339 c.Check(cli.startService(), NotNil)
340 c.Assert(cli.setupPostalService(), IsNil)
341 c.Assert(cli.startPostalService(), NotNil)
342}
343
344func (cs *clientSuite) TestStartServiceErrorsOnBusDialPostalFail(c *C) {
345 cli := NewPushClient(cs.configPath, cs.leveldbPath)
346 cli.log = cs.log
347 cli.pushServiceEndpoint = testibus.NewTestingEndpoint(condition.Work(true), nil)
348 cli.postalServiceEndpoint = testibus.NewTestingEndpoint(condition.Work(false), nil)
349 c.Check(cli.startService(), IsNil)
350 c.Assert(cli.setupPostalService(), IsNil)
351 c.Assert(cli.startPostalService(), NotNil)
317}352}
318353
319/*****************************************************************354/*****************************************************************
@@ -386,6 +421,8 @@
386 cli.systemImageEndp = siEndp421 cli.systemImageEndp = siEndp
387422
388 c.Assert(cli.takeTheBus(), IsNil)423 c.Assert(cli.takeTheBus(), IsNil)
424 c.Assert(cli.setupPostalService(), IsNil)
425 c.Assert(cli.takePostalServiceBus(), IsNil)
389 // the notifications and urldispatcher endpoints retried until connected426 // the notifications and urldispatcher endpoints retried until connected
390 c.Check(nCond.OK(), Equals, true)427 c.Check(nCond.OK(), Equals, true)
391 c.Check(uCond.OK(), Equals, true)428 c.Check(uCond.OK(), Equals, true)
@@ -415,6 +452,8 @@
415 cli.systemImageEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false))452 cli.systemImageEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false))
416453
417 c.Check(cli.takeTheBus(), NotNil)454 c.Check(cli.takeTheBus(), NotNil)
455 c.Assert(cli.setupPostalService(), IsNil)
456 c.Assert(cli.takePostalServiceBus(), NotNil)
418 c.Check(cli.actionsCh, IsNil)457 c.Check(cli.actionsCh, IsNil)
419}458}
420459
@@ -608,6 +647,8 @@
608 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))647 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
609 cli.notificationsEndp = endp648 cli.notificationsEndp = endp
610 cli.log = cs.log649 cli.log = cs.log
650 cli.postalServiceEndpoint = testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
651 cli.setupPostalService()
611 c.Check(cli.handleBroadcastNotification(positiveBroadcastNotification), IsNil)652 c.Check(cli.handleBroadcastNotification(positiveBroadcastNotification), IsNil)
612 // check we sent the notification653 // check we sent the notification
613 args := testibus.GetCallArgs(endp)654 args := testibus.GetCallArgs(endp)
@@ -635,6 +676,8 @@
635 cli.log = cs.log676 cli.log = cs.log
636 endp := testibus.NewTestingEndpoint(nil, condition.Work(false))677 endp := testibus.NewTestingEndpoint(nil, condition.Work(false))
637 cli.notificationsEndp = endp678 cli.notificationsEndp = endp
679 cli.postalServiceEndpoint = testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
680 cli.setupPostalService()
638 c.Check(cli.handleBroadcastNotification(positiveBroadcastNotification), NotNil)681 c.Check(cli.handleBroadcastNotification(positiveBroadcastNotification), NotNil)
639}682}
640683
@@ -642,19 +685,24 @@
642 handleUnicastNotification tests685 handleUnicastNotification tests
643******************************************************************/686******************************************************************/
644687
645var notif = &protocol.Notification{AppId: "hello", Payload: []byte(`{"url": "xyzzy"}`), MsgId: "42"}688var payload = `{"message": "aGVsbG8=", "notification": {"card": {"icon": "icon-value", "summary": "summary-value", "body": "body-value", "actions": []}}}`
689var notif = &protocol.Notification{AppId: "hello", Payload: []byte(payload), MsgId: "42"}
646690
647func (cs *clientSuite) TestHandleUcastNotification(c *C) {691func (cs *clientSuite) TestHandleUcastNotification(c *C) {
648 cli := NewPushClient(cs.configPath, cs.leveldbPath)692 cli := NewPushClient(cs.configPath, cs.leveldbPath)
649 svcEndp := testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), uint32(1))693 svcEndp := testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), uint32(1))
694 postEndp := testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), uint32(1))
650 cli.log = cs.log695 cli.log = cs.log
651 cli.serviceEndpoint = svcEndp696 cli.pushServiceEndpoint = svcEndp
697 cli.postalServiceEndpoint = postEndp
652 notsEndp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))698 notsEndp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
653 cli.notificationsEndp = notsEndp699 cli.notificationsEndp = notsEndp
654 c.Assert(cli.startService(), IsNil)700 c.Assert(cli.startService(), IsNil)
701 c.Assert(cli.setupPostalService(), IsNil)
702 c.Assert(cli.startPostalService(), IsNil)
655 c.Check(cli.handleUnicastNotification(notif), IsNil)703 c.Check(cli.handleUnicastNotification(notif), IsNil)
656 // check we sent the notification704 // check we sent the notification
657 args := testibus.GetCallArgs(svcEndp)705 args := testibus.GetCallArgs(postEndp)
658 c.Assert(len(args), Not(Equals), 0)706 c.Assert(len(args), Not(Equals), 0)
659 c.Check(args[len(args)-1].Member, Equals, "::Signal")707 c.Check(args[len(args)-1].Member, Equals, "::Signal")
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".*`)
@@ -675,14 +723,14 @@
675 args := testibus.GetCallArgs(endp)723 args := testibus.GetCallArgs(endp)
676 c.Assert(args, HasLen, 0)724 c.Assert(args, HasLen, 0)
677 // check we worked with the right action id725 // check we worked with the right action id
678 c.Check(cli.handleClick(ACTION_ID_BROADCAST), IsNil)726 c.Check(cli.handleClick(service.ACTION_ID_BROADCAST), IsNil)
679 // check we sent the notification727 // check we sent the notification
680 args = testibus.GetCallArgs(endp)728 args = testibus.GetCallArgs(endp)
681 c.Assert(args, HasLen, 1)729 c.Assert(args, HasLen, 1)
682 c.Check(args[0].Member, Equals, "DispatchURL")730 c.Check(args[0].Member, Equals, "DispatchURL")
683 c.Check(args[0].Args, DeepEquals, []interface{}{system_update_url})731 c.Check(args[0].Args, DeepEquals, []interface{}{service.SystemUpdateUrl})
684 // check we worked with the right action id732 // check we worked with the right action id
685 c.Check(cli.handleClick(ACTION_ID_SNOWFLAKE+"foo"), IsNil)733 c.Check(cli.handleClick(service.ACTION_ID_SNOWFLAKE+"foo"), IsNil)
686 // check we sent the notification734 // check we sent the notification
687 args = testibus.GetCallArgs(endp)735 args = testibus.GetCallArgs(endp)
688 c.Assert(args, HasLen, 2)736 c.Assert(args, HasLen, 2)
@@ -810,6 +858,8 @@
810 cli.connectivityEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true),858 cli.connectivityEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true),
811 uint32(networkmanager.ConnectedGlobal))859 uint32(networkmanager.ConnectedGlobal))
812 cli.systemImageInfo = siInfoRes860 cli.systemImageInfo = siInfoRes
861 cli.postalServiceEndpoint = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), uint32(1))
862 cli.setupPostalService()
813 c.Assert(cli.initSession(), IsNil)863 c.Assert(cli.initSession(), IsNil)
814864
815 cli.session.BroadcastCh = make(chan *session.BroadcastNotification)865 cli.session.BroadcastCh = make(chan *session.BroadcastNotification)
@@ -829,7 +879,7 @@
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$")
830880
831 // * actionsCh to the click handler/url dispatcher881 // * actionsCh to the click handler/url dispatcher
832 aCh <- notifications.RawActionReply{ActionId: ACTION_ID_BROADCAST}882 aCh <- notifications.RawActionReply{ActionId: service.ACTION_ID_BROADCAST}
833 tick()883 tick()
834 uargs := testibus.GetCallArgs(cli.urlDispatcherEndp)884 uargs := testibus.GetCallArgs(cli.urlDispatcherEndp)
835 c.Assert(uargs, HasLen, 1)885 c.Assert(uargs, HasLen, 1)
@@ -879,7 +929,7 @@
879 cli := NewPushClient(cs.configPath, cs.leveldbPath)929 cli := NewPushClient(cs.configPath, cs.leveldbPath)
880 // before start, everything sucks:930 // before start, everything sucks:
881 // no service,931 // no service,
882 c.Check(cli.service, IsNil)932 c.Check(cli.pushService, IsNil)
883 // no config,933 // no config,
884 c.Check(string(cli.config.Addr), Equals, "")934 c.Check(string(cli.config.Addr), Equals, "")
885 // no device id,935 // no device id,
@@ -904,9 +954,10 @@
904 // and a bus,954 // and a bus,
905 c.Check(cli.notificationsEndp, NotNil)955 c.Check(cli.notificationsEndp, NotNil)
906 // and a service,956 // and a service,
907 c.Check(cli.service, NotNil)957 c.Check(cli.pushService, NotNil)
908 // and everthying us just peachy!958 // and everthying us just peachy!
909 cli.service.Stop() // cleanup959 cli.pushService.Stop() // cleanup
960 cli.postalService.Stop() // cleanup
910}961}
911962
912func (cs *clientSuite) TestStartCanFail(c *C) {963func (cs *clientSuite) TestStartCanFail(c *C) {
@@ -917,37 +968,41 @@
917 c.Check(err, NotNil)968 c.Check(err, NotNil)
918}969}
919970
920func (cs *clientSuite) TestMessageHandler(c *C) {971func (cs *clientSuite) TestInitSessionErr(c *C) {
921 cli := NewPushClient(cs.configPath, cs.leveldbPath)972 cli := NewPushClient(cs.configPath, cs.leveldbPath)
922 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))973 cli.log = cs.log
923 cli.notificationsEndp = endp974 cli.systemImageInfo = siInfoRes
924 cli.log = cs.log975 // change the cli.pem value so initSession fails
925 err := cli.messageHandler([]byte(`{"icon": "icon-value", "summary": "summary-value", "body": "body-value"}`))976 cli.pem = []byte("foo")
926 c.Assert(err, IsNil)977 c.Assert(cli.initSession(), NotNil)
927 args := testibus.GetCallArgs(endp)978}
928 c.Assert(args, HasLen, 1)979
929 c.Check(args[0].Member, Equals, "Notify")980/*****************************************************************
930 c.Check(args[0].Args[0], Equals, "ubuntu-push-client")981 getAuthorization() tests
931 c.Check(args[0].Args[2], Equals, "icon-value")982******************************************************************/
932 c.Check(args[0].Args[3], Equals, "summary-value")983
933 c.Check(args[0].Args[4], Equals, "body-value")984func (cs *clientSuite) TestGetAuthorizationIgnoresErrors(c *C) {
934}985 cli := NewPushClient(cs.configPath, cs.leveldbPath)
935986 cli.configure()
936func (cs *clientSuite) TestMessageHandlerReportsUnmarshalErrors(c *C) {987 cli.config.AuthHelper = "/no/such/executable"
937 cli := NewPushClient(cs.configPath, cs.leveldbPath)988
938 cli.log = cs.log989 c.Check(cli.getAuthorization("xyzzy://"), Equals, "")
939990}
940 err := cli.messageHandler([]byte(`{"broken`))991
941 c.Check(err, NotNil)992func (cs *clientSuite) TestGetAuthorizationGetsIt(c *C) {
942 c.Check(cs.log.Captured(), Matches, "(?msi).*unable to unmarshal message:.*")993 cli := NewPushClient(cs.configPath, cs.leveldbPath)
943}994 cli.configure()
944995 cli.config.AuthHelper = helpers.ScriptAbsPath("dummyauth.sh")
945func (cs *clientSuite) TestMessageHandlerReportsFailedNotifies(c *C) {996
946 cli := NewPushClient(cs.configPath, cs.leveldbPath)997 c.Check(cli.getAuthorization("xyzzy://"), Equals, "hello xyzzy://")
947 endp := testibus.NewTestingEndpoint(nil, condition.Work(false))998}
948 cli.notificationsEndp = endp999
949 cli.log = cs.log1000func (cs *clientSuite) TestGetAuthorizationWorksIfUnsetOrNil(c *C) {
950 err := cli.messageHandler([]byte(`{}`))1001 cli := NewPushClient(cs.configPath, cs.leveldbPath)
951 c.Assert(err, NotNil)1002 cli.log = cs.log
952 c.Check(cs.log.Captured(), Matches, "(?msi).*showing notification: no way$")1003
1004 c.Assert(cli.config, NotNil)
1005 c.Check(cli.getAuthorization("xyzzy://"), Equals, "")
1006 cli.configure()
1007 c.Check(cli.getAuthorization("xyzzy://"), Equals, "")
953}1008}
9541009
=== modified file 'client/gethosts/gethost_test.go'
--- client/gethosts/gethost_test.go 2014-05-02 10:42:16 +0000
+++ client/gethosts/gethost_test.go 2014-07-02 13:12:36 +0000
@@ -59,7 +59,7 @@
59 res, err := gh.Get()59 res, err := gh.Get()
60 c.Assert(err, IsNil)60 c.Assert(err, IsNil)
61 c.Check(*res, DeepEquals,61 c.Check(*res, DeepEquals,
62 Host{Domain: "example.com", Hosts: []string{"http://c1130408a700afe0"}})62 Host{Domain: "example.com", Hosts: []string{"http://bdd2ae7116c85a45"}})
63}63}
6464
65func (s *getHostsSuite) TestGetTimeout(c *C) {65func (s *getHostsSuite) TestGetTimeout(c *C) {
6666
=== added file 'client/service/common.go'
--- client/service/common.go 1970-01-01 00:00:00 +0000
+++ client/service/common.go 2014-07-02 13:12:36 +0000
@@ -0,0 +1,99 @@
1/*
2 Copyright 2014 Canonical Ltd.
3
4 This program is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License version 3, as published
6 by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranties of
10 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17// package service implements the dbus-level service with which client
18// applications are expected to interact.
19package service
20
21import (
22 "errors"
23 "sync"
24
25 "launchpad.net/ubuntu-push/bus"
26 "launchpad.net/ubuntu-push/logger"
27)
28
29type DBusService struct {
30 lock sync.RWMutex
31 state ServiceState
32 Log logger.Logger
33 Bus bus.Endpoint
34}
35
36// the service can be in a numnber of states
37type ServiceState uint8
38
39const (
40 StateUnknown ServiceState = iota
41 StateRunning // Start() has been successfully called
42 StateFinished // Stop() has been successfully called
43)
44
45var (
46 NotConfigured = errors.New("not configured")
47 AlreadyStarted = errors.New("already started")
48 BadArgCount = errors.New("Wrong number of arguments")
49 BadArgType = errors.New("Bad argument type")
50)
51
52// IsRunning() returns whether the service's state is StateRunning
53func (svc *DBusService) IsRunning() bool {
54 svc.lock.RLock()
55 defer svc.lock.RUnlock()
56 return svc.state == StateRunning
57}
58
59// Start() dials the bus, grab the name, and listens for method calls.
60func (svc *DBusService) Start(dispatchMap bus.DispatchMap, busAddr bus.Address) error {
61 svc.lock.Lock()
62 defer svc.lock.Unlock()
63 if svc.state != StateUnknown {
64 return AlreadyStarted
65 }
66 if svc.Log == nil || svc.Bus == nil {
67 return NotConfigured
68 }
69 err := svc.Bus.Dial()
70 if err != nil {
71 return err
72 }
73 ch := svc.Bus.GrabName(true)
74 log := svc.Log
75 go func() {
76 for err := range ch {
77 if !svc.IsRunning() {
78 break
79 }
80 if err != nil {
81 log.Fatalf("name channel for %s got: %v",
82 busAddr.Name, err)
83 }
84 }
85 }()
86 svc.Bus.WatchMethod(dispatchMap, "/*", svc)
87 svc.state = StateRunning
88 return nil
89}
90
91// Stop() closes the bus and sets the state to StateFinished
92func (svc *DBusService) Stop() {
93 svc.lock.Lock()
94 defer svc.lock.Unlock()
95 if svc.Bus != nil {
96 svc.Bus.Close()
97 }
98 svc.state = StateFinished
99}
0100
=== added file 'client/service/postal.go'
--- client/service/postal.go 1970-01-01 00:00:00 +0000
+++ client/service/postal.go 2014-07-02 13:12:36 +0000
@@ -0,0 +1,181 @@
1/*
2 Copyright 2013-2014 Canonical Ltd.
3
4 This program is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License version 3, as published
6 by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranties of
10 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17package service
18
19import (
20 "strings"
21
22 "code.google.com/p/go-uuid/uuid"
23 "launchpad.net/go-dbus/v1"
24
25 "launchpad.net/ubuntu-push/bus"
26 "launchpad.net/ubuntu-push/bus/notifications"
27 "launchpad.net/ubuntu-push/launch_helper"
28 "launchpad.net/ubuntu-push/logger"
29 "launchpad.net/ubuntu-push/messaging"
30 "launchpad.net/ubuntu-push/nih"
31 "launchpad.net/ubuntu-push/util"
32)
33
34// PostalService is the dbus api
35type PostalService struct {
36 DBusService
37 mbox map[string][]string
38 msgHandler func(string, string, *launch_helper.HelperOutput) error
39 HelperLauncher launch_helper.HelperLauncher
40 messagingMenu *messaging.MessagingMenu
41 notificationsEndp bus.Endpoint
42}
43
44var (
45 PostalServiceBusAddress = bus.Address{
46 Interface: "com.ubuntu.Postal",
47 Path: "/com/ubuntu/Postal",
48 Name: "com.ubuntu.Postal",
49 }
50)
51
52var (
53 SystemUpdateUrl = "settings:///system/system-update"
54 ACTION_ID_SNOWFLAKE = "::ubuntu-push-client::"
55 ACTION_ID_BROADCAST = ACTION_ID_SNOWFLAKE + SystemUpdateUrl
56)
57
58// NewPostalService() builds a new service and returns it.
59func NewPostalService(busEndp bus.Endpoint, notificationsEndp bus.Endpoint, log logger.Logger) *PostalService {
60 var svc = &PostalService{}
61 svc.Log = log
62 svc.Bus = busEndp
63 svc.messagingMenu = messaging.New(log)
64 svc.HelperLauncher = launch_helper.NewTrivialHelperLauncher(log)
65 svc.notificationsEndp = notificationsEndp
66 svc.msgHandler = svc.messageHandler
67 return svc
68}
69
70// SetMessageHandler() sets the message-handling callback
71func (svc *PostalService) SetMessageHandler(callback func(string, string, *launch_helper.HelperOutput) error) {
72 svc.lock.RLock()
73 defer svc.lock.RUnlock()
74 svc.msgHandler = callback
75}
76
77// GetMessageHandler() returns the (possibly nil) messaging handler callback
78func (svc *PostalService) GetMessageHandler() func(string, string, *launch_helper.HelperOutput) error {
79 svc.lock.RLock()
80 defer svc.lock.RUnlock()
81 return svc.msgHandler
82}
83
84// Start() dials the bus, grab the name, and listens for method calls.
85func (svc *PostalService) Start() error {
86 return svc.DBusService.Start(bus.DispatchMap{
87 "Notifications": svc.notifications,
88 "Inject": svc.inject,
89 }, PostalServiceBusAddress)
90}
91
92func (svc *PostalService) TakeTheBus() (<-chan notifications.RawActionReply, error) {
93 iniCh := make(chan uint32)
94 go func() { iniCh <- util.NewAutoRedialer(svc.notificationsEndp).Redial() }()
95 <-iniCh
96 actionsCh, err := notifications.Raw(svc.notificationsEndp, svc.Log).WatchActions()
97 return actionsCh, err
98}
99
100func (svc *PostalService) notifications(path string, args, _ []interface{}) ([]interface{}, error) {
101 if len(args) != 0 {
102 return nil, BadArgCount
103 }
104 appname := string(nih.Unquote([]byte(path[strings.LastIndex(path, "/")+1:])))
105
106 svc.lock.Lock()
107 defer svc.lock.Unlock()
108
109 if svc.mbox == nil {
110 return []interface{}{[]string(nil)}, nil
111 }
112 msgs := svc.mbox[appname]
113 delete(svc.mbox, appname)
114
115 return []interface{}{msgs}, nil
116}
117
118var newNid = uuid.New
119
120func (svc *PostalService) inject(path string, args, _ []interface{}) ([]interface{}, error) {
121 if len(args) != 1 {
122 return nil, BadArgCount
123 }
124 notif, ok := args[0].(string)
125 if !ok {
126 return nil, BadArgType
127 }
128 appname := string(nih.Unquote([]byte(path[strings.LastIndex(path, "/")+1:])))
129
130 nid := newNid()
131
132 return nil, svc.Inject(appname, nid, notif)
133}
134
135// Inject() signals to an application over dbus that a notification
136// has arrived.
137func (svc *PostalService) Inject(appname string, nid string, notif string) error {
138 svc.lock.Lock()
139 defer svc.lock.Unlock()
140 if svc.mbox == nil {
141 svc.mbox = make(map[string][]string)
142 }
143 output := svc.HelperLauncher.Run(appname, []byte(notif))
144 // XXX also track the nid in the mbox
145 svc.mbox[appname] = append(svc.mbox[appname], string(output.Message))
146
147 if svc.msgHandler != nil {
148 err := svc.msgHandler(appname, nid, output)
149 if err != nil {
150 svc.DBusService.Log.Errorf("msgHandler returned %v", err)
151 return err
152 }
153 svc.DBusService.Log.Debugf("call to msgHandler successful")
154 }
155
156 return svc.Bus.Signal("Notification", "/"+string(nih.Quote([]byte(appname))), []interface{}{appname})
157}
158
159func (svc *PostalService) messageHandler(appname string, nid string, output *launch_helper.HelperOutput) error {
160 svc.messagingMenu.Present(appname, nid, output.Notification)
161 nots := notifications.Raw(svc.notificationsEndp, svc.Log)
162 _, err := nots.Present(appname, nid, output.Notification)
163
164 return err
165}
166
167func (svc *PostalService) SendNotification(action_id, icon, summary, body string) (uint32, error) {
168 a := []string{action_id, "Switch to app"} // action value not visible on the phone
169 h := map[string]*dbus.Variant{"x-canonical-switch-to-application": &dbus.Variant{true}}
170 nots := notifications.Raw(svc.notificationsEndp, svc.Log)
171 return nots.Notify(
172 "ubuntu-push-client", // app name
173 uint32(0), // id
174 icon, // icon
175 summary, // summary
176 body, // body
177 a, // actions
178 h, // hints
179 int32(10*1000), // timeout (ms)
180 )
181}
0182
=== added file 'client/service/postal_test.go'
--- client/service/postal_test.go 1970-01-01 00:00:00 +0000
+++ client/service/postal_test.go 2014-07-02 13:12:36 +0000
@@ -0,0 +1,252 @@
1/*
2 Copyright 2014 Canonical Ltd.
3
4 This program is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License version 3, as published
6 by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranties of
10 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17package service
18
19import (
20 "errors"
21
22 . "launchpad.net/gocheck"
23
24 "launchpad.net/ubuntu-push/bus"
25 testibus "launchpad.net/ubuntu-push/bus/testing"
26 "launchpad.net/ubuntu-push/launch_helper"
27 helpers "launchpad.net/ubuntu-push/testing"
28 "launchpad.net/ubuntu-push/testing/condition"
29)
30
31type postalSuite struct {
32 log *helpers.TestLogger
33 bus bus.Endpoint
34 notifBus bus.Endpoint
35}
36
37var _ = Suite(&postalSuite{})
38
39func (ss *postalSuite) SetUpTest(c *C) {
40 ss.log = helpers.NewTestLogger(c, "debug")
41 ss.bus = testibus.NewTestingEndpoint(condition.Work(true), nil)
42 ss.notifBus = testibus.NewTestingEndpoint(condition.Work(true), nil)
43}
44
45func (ss *postalSuite) TestStart(c *C) {
46 svc := NewPostalService(ss.bus, ss.notifBus, ss.log)
47 c.Check(svc.IsRunning(), Equals, false)
48 c.Check(svc.Start(), IsNil)
49 c.Check(svc.IsRunning(), Equals, true)
50 svc.Stop()
51}
52
53func (ss *postalSuite) TestStartTwice(c *C) {
54 svc := NewPostalService(ss.bus, ss.notifBus, ss.log)
55 c.Check(svc.Start(), IsNil)
56 c.Check(svc.Start(), Equals, AlreadyStarted)
57 svc.Stop()
58}
59
60func (ss *postalSuite) TestStartNoLog(c *C) {
61 svc := NewPostalService(ss.bus, ss.notifBus, nil)
62 c.Check(svc.Start(), Equals, NotConfigured)
63}
64
65func (ss *postalSuite) TestStartNoBus(c *C) {
66 svc := NewPostalService(nil, ss.notifBus, ss.log)
67 c.Check(svc.Start(), Equals, NotConfigured)
68}
69
70func (ss *postalSuite) TestTakeTheBustFail(c *C) {
71 nEndp := testibus.NewMultiValuedTestingEndpoint(condition.Work(true), condition.Work(false), []interface{}{uint32(1), "hello"})
72 svc := NewPostalService(ss.bus, nEndp, ss.log)
73 _, err := svc.TakeTheBus()
74 c.Check(err, NotNil)
75}
76
77func (ss *postalSuite) TestTakeTheBustOk(c *C) {
78 nEndp := testibus.NewMultiValuedTestingEndpoint(condition.Work(true), condition.Work(true), []interface{}{uint32(1), "hello"})
79 svc := NewPostalService(ss.bus, nEndp, ss.log)
80 _, err := svc.TakeTheBus()
81 c.Check(err, IsNil)
82}
83
84func (ss *postalSuite) TestStartFailsOnBusDialFailure(c *C) {
85 bus := testibus.NewTestingEndpoint(condition.Work(false), nil)
86 svc := NewPostalService(bus, ss.notifBus, ss.log)
87 c.Check(svc.Start(), ErrorMatches, `.*(?i)cond said no.*`)
88 svc.Stop()
89}
90
91func (ss *postalSuite) TestStartGrabsName(c *C) {
92 svc := NewPostalService(ss.bus, ss.notifBus, ss.log)
93 c.Assert(svc.Start(), IsNil)
94 callArgs := testibus.GetCallArgs(ss.bus)
95 defer svc.Stop()
96 c.Assert(callArgs, NotNil)
97 c.Check(callArgs[0].Member, Equals, "::GrabName")
98}
99
100func (ss *postalSuite) TestStopClosesBus(c *C) {
101 svc := NewPostalService(ss.bus, ss.notifBus, ss.log)
102 c.Assert(svc.Start(), IsNil)
103 svc.Stop()
104 callArgs := testibus.GetCallArgs(ss.bus)
105 c.Assert(callArgs, NotNil)
106 c.Check(callArgs[len(callArgs)-1].Member, Equals, "::Close")
107}
108
109//
110// Injection tests
111
112func (ss *postalSuite) TestInjectWorks(c *C) {
113 svc := NewPostalService(ss.bus, ss.notifBus, ss.log)
114 svc.msgHandler = nil
115 rvs, err := svc.inject("/hello", []interface{}{"world"}, nil)
116 c.Assert(err, IsNil)
117 c.Check(rvs, IsNil)
118 rvs, err = svc.inject("/hello", []interface{}{"there"}, nil)
119 c.Assert(err, IsNil)
120 c.Check(rvs, IsNil)
121 c.Assert(svc.mbox, HasLen, 1)
122 c.Assert(svc.mbox["hello"], HasLen, 2)
123 c.Check(svc.mbox["hello"][0], Equals, "world")
124 c.Check(svc.mbox["hello"][1], Equals, "there")
125
126 // and check it fired the right signal (twice)
127 callArgs := testibus.GetCallArgs(ss.bus)
128 c.Assert(callArgs, HasLen, 2)
129 c.Check(callArgs[0].Member, Equals, "::Signal")
130 c.Check(callArgs[0].Args, DeepEquals, []interface{}{"Notification", "/hello", []interface{}{"hello"}})
131 c.Check(callArgs[1], DeepEquals, callArgs[0])
132}
133
134func (ss *postalSuite) TestInjectFailsIfInjectFails(c *C) {
135 bus := testibus.NewTestingEndpoint(condition.Work(true),
136 condition.Work(false))
137 svc := NewPostalService(bus, ss.notifBus, ss.log)
138 svc.SetMessageHandler(func(string, string, *launch_helper.HelperOutput) error { return errors.New("fail") })
139 _, err := svc.inject("/hello", []interface{}{"xyzzy"}, nil)
140 c.Check(err, NotNil)
141}
142
143func (ss *postalSuite) TestInjectFailsIfBadArgs(c *C) {
144 for i, s := range []struct {
145 args []interface{}
146 errt error
147 }{
148 {nil, BadArgCount},
149 {[]interface{}{}, BadArgCount},
150 {[]interface{}{1}, BadArgType},
151 {[]interface{}{1, 2}, BadArgCount},
152 } {
153 reg, err := new(PostalService).inject("", s.args, nil)
154 c.Check(reg, IsNil, Commentf("iteration #%d", i))
155 c.Check(err, Equals, s.errt, Commentf("iteration #%d", i))
156 }
157}
158
159//
160// Notifications tests
161func (ss *postalSuite) TestNotificationsWorks(c *C) {
162 svc := NewPostalService(ss.bus, ss.notifBus, ss.log)
163 nots, err := svc.notifications("/hello", nil, nil)
164 c.Assert(err, IsNil)
165 c.Assert(nots, NotNil)
166 c.Assert(nots, HasLen, 1)
167 c.Check(nots[0], HasLen, 0)
168 if svc.mbox == nil {
169 svc.mbox = make(map[string][]string)
170 }
171 svc.mbox["hello"] = append(svc.mbox["hello"], "this", "thing")
172 nots, err = svc.notifications("/hello", nil, nil)
173 c.Assert(err, IsNil)
174 c.Assert(nots, NotNil)
175 c.Assert(nots, HasLen, 1)
176 c.Check(nots[0], DeepEquals, []string{"this", "thing"})
177}
178
179func (ss *postalSuite) TestNotificationsFailsIfBadArgs(c *C) {
180 reg, err := new(PostalService).notifications("/foo", []interface{}{1}, nil)
181 c.Check(reg, IsNil)
182 c.Check(err, Equals, BadArgCount)
183}
184
185func (ss *postalSuite) TestMessageHandlerPublicAPI(c *C) {
186 svc := new(PostalService)
187 c.Assert(svc.msgHandler, IsNil)
188 var ext = &launch_helper.HelperOutput{}
189 e := errors.New("Hello")
190 f := func(app string, nid string, s *launch_helper.HelperOutput) error { ext = s; return e }
191 c.Check(svc.GetMessageHandler(), IsNil)
192 svc.SetMessageHandler(f)
193 c.Check(svc.GetMessageHandler(), NotNil)
194 hOutput := &launch_helper.HelperOutput{[]byte("37"), nil}
195 c.Check(svc.msgHandler("", "", hOutput), Equals, e)
196 c.Check(ext, DeepEquals, hOutput)
197}
198
199func (ss *postalSuite) TestInjectCallsMessageHandler(c *C) {
200 var ext = &launch_helper.HelperOutput{}
201 svc := NewPostalService(ss.bus, ss.notifBus, ss.log)
202 f := func(app string, nid string, s *launch_helper.HelperOutput) error { ext = s; return nil }
203 svc.SetMessageHandler(f)
204 c.Check(svc.Inject("stuff", "thing", "{}"), IsNil)
205 c.Check(ext, DeepEquals, &launch_helper.HelperOutput{})
206 err := errors.New("ouch")
207 svc.SetMessageHandler(func(string, string, *launch_helper.HelperOutput) error { return err })
208 c.Check(svc.Inject("stuff", "", "{}"), Equals, err)
209}
210
211func (ss *postalSuite) TestMessageHandler(c *C) {
212 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
213 svc := NewPostalService(ss.bus, endp, ss.log)
214 card := &launch_helper.Card{Icon: "icon-value", Summary: "summary-value", Body: "body-value", Popup: true}
215 output := &launch_helper.HelperOutput{Notification: &launch_helper.Notification{Card: card}}
216 err := svc.messageHandler("xyzzy", "", output)
217 c.Assert(err, IsNil)
218 args := testibus.GetCallArgs(endp)
219 c.Assert(args, HasLen, 1)
220 c.Check(args[0].Member, Equals, "Notify")
221 c.Check(args[0].Args[0], Equals, "xyzzy")
222 c.Check(args[0].Args[2], Equals, "icon-value")
223 c.Check(args[0].Args[3], Equals, "summary-value")
224 c.Check(args[0].Args[4], Equals, "body-value")
225}
226
227func (ss *postalSuite) TestMessageHandlerReportsFailedNotifies(c *C) {
228 endp := testibus.NewTestingEndpoint(nil, condition.Work(false))
229 svc := NewPostalService(ss.bus, endp, ss.log)
230 card := &launch_helper.Card{Icon: "icon-value", Summary: "summary-value", Body: "body-value", Popup: true}
231 notif := &launch_helper.Notification{Card: card}
232 output := &launch_helper.HelperOutput{Notification: notif}
233 err := svc.messageHandler("", "", output)
234 c.Assert(err, NotNil)
235}
236
237func (ss *postalSuite) TestMessageHandlerReportsButIgnoresUnmarshalErrors(c *C) {
238 svc := NewPostalService(ss.bus, ss.notifBus, ss.log)
239 output := &launch_helper.HelperOutput{[]byte(`broken`), nil}
240 err := svc.messageHandler("", "", output)
241 c.Check(err, IsNil)
242 c.Check(ss.log.Captured(), Matches, "(?msi).*skipping notification: nil.*")
243}
244
245func (ss *postalSuite) TestMessageHandlerReportsButIgnoresNilNotifies(c *C) {
246 endp := testibus.NewTestingEndpoint(nil, condition.Work(false))
247 svc := NewPostalService(ss.bus, endp, ss.log)
248 output := &launch_helper.HelperOutput{[]byte(`{}`), nil}
249 err := svc.messageHandler("", "", output)
250 c.Assert(err, IsNil)
251 c.Check(ss.log.Captured(), Matches, "(?msi).*skipping notification: nil.*")
252}
0253
=== modified file 'client/service/service.go'
--- client/service/service.go 2014-05-21 10:03:18 +0000
+++ client/service/service.go 2014-07-02 13:12:36 +0000
@@ -14,196 +14,184 @@
14 with this program. If not, see <http://www.gnu.org/licenses/>.14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/15*/
1616
17// package service implements the dbus-level service with which client
18// applications are expected to interact.
19package service17package service
2018
21import (19import (
20 "bytes"
21 "encoding/json"
22 "errors"22 "errors"
23 "fmt"
24 "io/ioutil"
25 "net/http"
23 "os"26 "os"
24 "sync"27 "strings"
2528
26 "launchpad.net/ubuntu-push/bus"29 "launchpad.net/ubuntu-push/bus"
30 http13 "launchpad.net/ubuntu-push/http13client"
27 "launchpad.net/ubuntu-push/logger"31 "launchpad.net/ubuntu-push/logger"
32 "launchpad.net/ubuntu-push/nih"
28)33)
2934
30// Service is the dbus api35// PushService is the dbus api
31type Service struct {36type PushService struct {
32 lock sync.RWMutex37 DBusService
33 state ServiceState38 regURL string
34 mbox map[string][]string39 deviceId string
35 msgHandler func([]byte) error40 authGetter func(string) string
36 Log logger.Logger41 httpCli http13.Client
37 Bus bus.Endpoint
38}42}
3943
40// the service can be in a numnber of states
41type ServiceState uint8
42
43const (
44 StateUnknown ServiceState = iota
45 StateRunning // Start() has been successfully called
46 StateFinished // Stop() has been successfully called
47)
48
49var (44var (
50 NotConfigured = errors.New("not configured")45 PushServiceBusAddress = bus.Address{
51 AlreadyStarted = errors.New("already started")
52 BusAddress = bus.Address{
53 Interface: "com.ubuntu.PushNotifications",46 Interface: "com.ubuntu.PushNotifications",
54 Path: "/com/ubuntu/PushNotifications",47 Path: "/com/ubuntu/PushNotifications",
55 Name: "com.ubuntu.PushNotifications",48 Name: "com.ubuntu.PushNotifications",
56 }49 }
57)50)
5851
59// NewService() builds a new service and returns it.52// NewPushService() builds a new service and returns it.
60func NewService(bus bus.Endpoint, log logger.Logger) *Service {53func NewPushService(bus bus.Endpoint, log logger.Logger) *PushService {
61 return &Service{Log: log, Bus: bus}54 var svc = &PushService{}
62}55 svc.Log = log
6356 svc.Bus = bus
64// SetMessageHandler() sets the message-handling callback57 return svc
65func (svc *Service) SetMessageHandler(callback func([]byte) error) {58}
66 svc.lock.Lock()59
67 defer svc.lock.Unlock()60// SetRegistrationURL() sets the registration url for the service
68 svc.msgHandler = callback61func (svc *PushService) SetRegistrationURL(url string) {
69}62 svc.lock.Lock()
7063 defer svc.lock.Unlock()
71// GetMessageHandler() returns the (possibly nil) messaging handler callback64 svc.regURL = url
72func (svc *Service) GetMessageHandler() func([]byte) error {65}
73 svc.lock.RLock()66
74 defer svc.lock.RUnlock()67// SetAuthGetter() sets the authorization getter for the service
75 return svc.msgHandler68func (svc *PushService) SetAuthGetter(authGetter func(string) string) {
76}69 svc.lock.Lock()
7770 defer svc.lock.Unlock()
78// IsRunning() returns whether the service's state is StateRunning71 svc.authGetter = authGetter
79func (svc *Service) IsRunning() bool {72}
80 svc.lock.RLock()73
81 defer svc.lock.RUnlock()74// getRegistrationAuthorization() returns the authorization header for
82 return svc.state == StateRunning75// POSTing to the registration HTTP endpoint
83}76//
8477// (this is for calling with the lock held)
85// Start() dials the bus, grab the name, and listens for method calls.78func (svc *PushService) getRegistrationAuthorization() string {
86func (svc *Service) Start() error {79 if svc.authGetter != nil && svc.regURL != "" {
87 svc.lock.Lock()80 return svc.authGetter(svc.regURL)
88 defer svc.lock.Unlock()81 } else {
89 if svc.state != StateUnknown {82 return ""
90 return AlreadyStarted83 }
91 }84}
92 if svc.Log == nil || svc.Bus == nil {85
93 return NotConfigured86// GetRegistrationAuthorization() returns the authorization header for
94 }87// POSTing to the registration HTTP endpoint
95 err := svc.Bus.Dial()88func (svc *PushService) GetRegistrationAuthorization() string {
96 if err != nil {89 svc.lock.RLock()
97 return err90 defer svc.lock.RUnlock()
98 }91 return svc.getRegistrationAuthorization()
99 ch := svc.Bus.GrabName(true)92}
100 log := svc.Log93
101 go func() {94// SetDeviceId() sets the device id
102 for err := range ch {95func (svc *PushService) SetDeviceId(deviceId string) {
103 if !svc.IsRunning() {96 svc.lock.Lock()
104 break97 defer svc.lock.Unlock()
105 }98 svc.deviceId = deviceId
106 if err != nil {99}
107 log.Fatalf("name channel for %s got: %v",100
108 BusAddress.Name, err)101// GetDeviceId() returns the device id
109 }102func (svc *PushService) GetDeviceId() string {
110 }103 svc.lock.RLock()
111 }()104 defer svc.lock.RUnlock()
112 svc.Bus.WatchMethod(bus.DispatchMap{105 return svc.deviceId
113 "Register": svc.register,106}
114 "Notifications": svc.notifications,107
115 "Inject": svc.inject,108func (svc *PushService) Start() error {
116 }, svc)109 return svc.DBusService.Start(bus.DispatchMap{
117 svc.state = StateRunning110 "Register": svc.register,
118 return nil111 }, PushServiceBusAddress)
119}
120
121// Stop() closes the bus and sets the state to StateFinished
122func (svc *Service) Stop() {
123 svc.lock.Lock()
124 defer svc.lock.Unlock()
125 if svc.Bus != nil {
126 svc.Bus.Close()
127 }
128 svc.state = StateFinished
129}112}
130113
131var (114var (
132 BadArgCount = errors.New("Wrong number of arguments")115 BadServer = errors.New("Bad server")
133 BadArgType = errors.New("Bad argument type")116 BadRequest = errors.New("Bad request")
117 BadToken = errors.New("Bad token")
118 BadAuth = errors.New("Bad auth")
134)119)
135120
136func (svc *Service) register(args []interface{}, _ []interface{}) ([]interface{}, error) {121type registrationRequest struct {
137 if len(args) != 1 {122 DeviceId string `json:"deviceid"`
138 return nil, BadArgCount123 AppId string `json:"appid"`
139 }124}
140 appname, ok := args[0].(string)125
141 if !ok {126type registrationReply struct {
142 return nil, BadArgType127 Token string `json:"token"` // the bit we're after
143 }128 Ok bool `json:"ok"` // only ever true or absent
144129 Error string `json:"error"` // these two only used for debugging
145 rv := os.Getenv("PUSH_REG_" + appname)130 Message string `json:"message"` //
146 if rv == "" {131}
147 rv = "this-is-an-opaque-block-of-random-bits-i-promise"132
148 }133func (svc *PushService) register(path string, args, _ []interface{}) ([]interface{}, error) {
149134 svc.lock.RLock()
150 return []interface{}{rv}, nil135 defer svc.lock.RUnlock()
151}136 if len(args) != 0 {
152137 return nil, BadArgCount
153func (svc *Service) notifications(args []interface{}, _ []interface{}) ([]interface{}, error) {138 }
154 if len(args) != 1 {139 raw_appname := path[strings.LastIndex(path, "/")+1:]
155 return nil, BadArgCount140 appname := string(nih.Unquote([]byte(raw_appname)))
156 }141
157 appname, ok := args[0].(string)142 rv := os.Getenv("PUSH_REG_" + raw_appname)
158 if !ok {143 if rv != "" {
159 return nil, BadArgType144 return []interface{}{rv}, nil
160 }145 }
161146
162 svc.lock.Lock()147 req_body, err := json.Marshal(registrationRequest{svc.deviceId, appname})
163 defer svc.lock.Unlock()148 if err != nil {
164149 return nil, fmt.Errorf("unable to marshal register request body: %v", err)
165 if svc.mbox == nil {150 }
166 return []interface{}{[]string(nil)}, nil151 req, err := http13.NewRequest("POST", svc.regURL, bytes.NewReader(req_body))
167 }152 if err != nil {
168 msgs := svc.mbox[appname]153 return nil, fmt.Errorf("unable to build register request: %v", err)
169 delete(svc.mbox, appname)154 }
170155 auth := svc.getRegistrationAuthorization()
171 return []interface{}{msgs}, nil156 if auth == "" {
172}157 return nil, BadAuth
173158 }
174func (svc *Service) inject(args []interface{}, _ []interface{}) ([]interface{}, error) {159 req.Header.Add("Authorization", auth)
175 if len(args) != 2 {160 req.Header.Add("Content-Type", "application/json")
176 return nil, BadArgCount161
177 }162 resp, err := svc.httpCli.Do(req)
178 appname, ok := args[0].(string)163 if err != nil {
179 if !ok {164 return nil, fmt.Errorf("unable to request registration: %v", err)
180 return nil, BadArgType165 }
181 }166 defer resp.Body.Close()
182 notif, ok := args[1].(string)167 if resp.StatusCode != http.StatusOK {
183 if !ok {168 svc.Log.Errorf("register endpoint replied %d", resp.StatusCode)
184 return nil, BadArgType169 switch {
185 }170 case resp.StatusCode >= http.StatusInternalServerError:
186171 // XXX retry on 503
187 return nil, svc.Inject(appname, notif)172 return nil, BadServer
188}173 default:
189174 return nil, BadRequest
190// Inject() signals to an application over dbus that a notification
191// has arrived.
192func (svc *Service) Inject(appname string, notif string) error {
193 svc.lock.Lock()
194 defer svc.lock.Unlock()
195 if svc.mbox == nil {
196 svc.mbox = make(map[string][]string)
197 }
198 svc.mbox[appname] = append(svc.mbox[appname], notif)
199 if svc.msgHandler != nil {
200 err := svc.msgHandler([]byte(notif))
201 if err != nil {
202 svc.Log.Errorf("msgHandler returned %v", err)
203 return err
204 }175 }
205 svc.Log.Debugf("call to msgHandler successful")176 }
206 }177 // errors below here Can't Happen (tm).
207178 body, err := ioutil.ReadAll(resp.Body)
208 return svc.Bus.Signal("Notification", []interface{}{appname})179 if err != nil {
180 svc.Log.Errorf("Reading response body: %v", err)
181 return nil, err
182 }
183
184 var reply registrationReply
185 err = json.Unmarshal(body, &reply)
186 if err != nil {
187 svc.Log.Errorf("Unmarshalling response body: %v", err)
188 return nil, fmt.Errorf("unable to unmarshal register response: %v", err)
189 }
190
191 if !reply.Ok || reply.Token == "" {
192 svc.Log.Errorf("Unexpected response: %#v", reply)
193 return nil, BadToken
194 }
195
196 return []interface{}{reply.Token}, nil
209}197}
210198
=== modified file 'client/service/service_test.go'
--- client/service/service_test.go 2014-05-21 10:05:24 +0000
+++ client/service/service_test.go 2014-07-02 13:12:36 +0000
@@ -17,7 +17,10 @@
17package service17package service
1818
19import (19import (
20 "errors"20 "encoding/json"
21 "fmt"
22 "net/http"
23 "net/http/httptest"
21 "os"24 "os"
22 "testing"25 "testing"
2326
@@ -45,7 +48,7 @@
45}48}
4649
47func (ss *serviceSuite) TestStart(c *C) {50func (ss *serviceSuite) TestStart(c *C) {
48 svc := NewService(ss.bus, ss.log)51 svc := NewPushService(ss.bus, ss.log)
49 c.Check(svc.IsRunning(), Equals, false)52 c.Check(svc.IsRunning(), Equals, false)
50 c.Check(svc.Start(), IsNil)53 c.Check(svc.Start(), IsNil)
51 c.Check(svc.IsRunning(), Equals, true)54 c.Check(svc.IsRunning(), Equals, true)
@@ -53,31 +56,31 @@
53}56}
5457
55func (ss *serviceSuite) TestStartTwice(c *C) {58func (ss *serviceSuite) TestStartTwice(c *C) {
56 svc := NewService(ss.bus, ss.log)59 svc := NewPushService(ss.bus, ss.log)
57 c.Check(svc.Start(), IsNil)60 c.Check(svc.Start(), IsNil)
58 c.Check(svc.Start(), Equals, AlreadyStarted)61 c.Check(svc.Start(), Equals, AlreadyStarted)
59 svc.Stop()62 svc.Stop()
60}63}
6164
62func (ss *serviceSuite) TestStartNoLog(c *C) {65func (ss *serviceSuite) TestStartNoLog(c *C) {
63 svc := NewService(ss.bus, nil)66 svc := NewPushService(ss.bus, nil)
64 c.Check(svc.Start(), Equals, NotConfigured)67 c.Check(svc.Start(), Equals, NotConfigured)
65}68}
6669
67func (ss *serviceSuite) TestStartNoBus(c *C) {70func (ss *serviceSuite) TestStartNoBus(c *C) {
68 svc := NewService(nil, ss.log)71 svc := NewPushService(nil, ss.log)
69 c.Check(svc.Start(), Equals, NotConfigured)72 c.Check(svc.Start(), Equals, NotConfigured)
70}73}
7174
72func (ss *serviceSuite) TestStartFailsOnBusDialFailure(c *C) {75func (ss *serviceSuite) TestStartFailsOnBusDialFailure(c *C) {
73 bus := testibus.NewTestingEndpoint(condition.Work(false), nil)76 bus := testibus.NewTestingEndpoint(condition.Work(false), nil)
74 svc := NewService(bus, ss.log)77 svc := NewPushService(bus, ss.log)
75 c.Check(svc.Start(), ErrorMatches, `.*(?i)cond said no.*`)78 c.Check(svc.Start(), ErrorMatches, `.*(?i)cond said no.*`)
76 svc.Stop()79 svc.Stop()
77}80}
7881
79func (ss *serviceSuite) TestStartGrabsName(c *C) {82func (ss *serviceSuite) TestStartGrabsName(c *C) {
80 svc := NewService(ss.bus, ss.log)83 svc := NewPushService(ss.bus, ss.log)
81 c.Assert(svc.Start(), IsNil)84 c.Assert(svc.Start(), IsNil)
82 callArgs := testibus.GetCallArgs(ss.bus)85 callArgs := testibus.GetCallArgs(ss.bus)
83 defer svc.Stop()86 defer svc.Stop()
@@ -86,7 +89,7 @@
86}89}
8790
88func (ss *serviceSuite) TestStopClosesBus(c *C) {91func (ss *serviceSuite) TestStopClosesBus(c *C) {
89 svc := NewService(ss.bus, ss.log)92 svc := NewPushService(ss.bus, ss.log)
90 c.Assert(svc.Start(), IsNil)93 c.Assert(svc.Start(), IsNil)
91 svc.Stop()94 svc.Stop()
92 callArgs := testibus.GetCallArgs(ss.bus)95 callArgs := testibus.GetCallArgs(ss.bus)
@@ -96,36 +99,75 @@
9699
97// registration tests100// registration tests
98101
102func (ss *serviceSuite) TestSetRegURLWorks(c *C) {
103 svc := NewPushService(ss.bus, ss.log)
104 c.Check(svc.regURL, Equals, "")
105 svc.SetRegistrationURL("xyzzy://")
106 c.Check(svc.regURL, Equals, "xyzzy://")
107}
108
109func (ss *serviceSuite) TestSetAuthGetterWorks(c *C) {
110 svc := NewPushService(ss.bus, ss.log)
111 c.Check(svc.authGetter, IsNil)
112 f := func(string) string { return "" }
113 svc.SetAuthGetter(f)
114 c.Check(fmt.Sprintf("%#v", svc.authGetter), Equals, fmt.Sprintf("%#v", f))
115}
116
117func (ss *serviceSuite) TestGetRegAuthWorks(c *C) {
118 svc := NewPushService(ss.bus, ss.log)
119 svc.SetRegistrationURL("xyzzy://")
120 ch := make(chan string, 1)
121 f := func(s string) string { ch <- s; return "Auth " + s }
122 svc.SetAuthGetter(f)
123 c.Check(svc.getRegistrationAuthorization(), Equals, "Auth xyzzy://")
124 c.Assert(len(ch), Equals, 1)
125 c.Check(<-ch, Equals, "xyzzy://")
126}
127
128func (ss *serviceSuite) TestGetRegAuthDoesNotPanic(c *C) {
129 svc := NewPushService(ss.bus, ss.log)
130 c.Check(svc.getRegistrationAuthorization(), Equals, "")
131}
132
99func (ss *serviceSuite) TestRegistrationFailsIfBadArgs(c *C) {133func (ss *serviceSuite) TestRegistrationFailsIfBadArgs(c *C) {
100 for i, s := range []struct {134 reg, err := new(PushService).register("", []interface{}{1}, nil)
101 args []interface{}135 c.Check(reg, IsNil)
102 errt error136 c.Check(err, Equals, BadArgCount)
103 }{
104 {nil, BadArgCount}, // no args
105 {[]interface{}{}, BadArgCount}, // still no args
106 {[]interface{}{42}, BadArgType}, // bad arg type
107 {[]interface{}{1, 2}, BadArgCount}, // too many args
108 } {
109 reg, err := new(Service).register(s.args, nil)
110 c.Check(reg, IsNil, Commentf("iteration #%d", i))
111 c.Check(err, Equals, s.errt, Commentf("iteration #%d", i))
112 }
113}137}
114138
115func (ss *serviceSuite) TestRegistrationWorks(c *C) {139func (ss *serviceSuite) TestRegistrationWorks(c *C) {
116 reg, err := new(Service).register([]interface{}{"this"}, nil)140 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
141 buf := make([]byte, 256)
142 n, e := r.Body.Read(buf)
143 c.Assert(e, IsNil)
144 req := registrationRequest{}
145 c.Assert(json.Unmarshal(buf[:n], &req), IsNil)
146 c.Check(req, DeepEquals, registrationRequest{"fake-device-id", "an-app-id"})
147
148 w.Header().Set("Content-Type", "application/json")
149 fmt.Fprintln(w, `{"ok":true,"token":"blob-of-bytes"}`)
150 }))
151 defer ts.Close()
152
153 svc := NewPushService(ss.bus, ss.log)
154 svc.SetAuthGetter(func(string) string { return "tok" })
155 svc.SetRegistrationURL(ts.URL)
156 svc.SetDeviceId("fake-device-id")
157 // this'll check (un)quoting, too
158 reg, err := svc.register("/an_2dapp_2did", nil, nil)
159 c.Assert(err, IsNil)
117 c.Assert(reg, HasLen, 1)160 c.Assert(reg, HasLen, 1)
118 regs, ok := reg[0].(string)161 regs, ok := reg[0].(string)
119 c.Check(ok, Equals, true)162 c.Check(ok, Equals, true)
120 c.Check(regs, Not(Equals), "")163 c.Check(regs, Equals, "blob-of-bytes")
121 c.Check(err, IsNil)
122}164}
123165
124func (ss *serviceSuite) TestRegistrationOverrideWorks(c *C) {166func (ss *serviceSuite) TestRegistrationOverrideWorks(c *C) {
125 os.Setenv("PUSH_REG_stuff", "42")167 os.Setenv("PUSH_REG_stuff", "42")
126 defer os.Setenv("PUSH_REG_stuff", "")168 defer os.Setenv("PUSH_REG_stuff", "")
127169
128 reg, err := new(Service).register([]interface{}{"stuff"}, nil)170 reg, err := new(PushService).register("/stuff", nil, nil)
129 c.Assert(reg, HasLen, 1)171 c.Assert(reg, HasLen, 1)
130 regs, ok := reg[0].(string)172 regs, ok := reg[0].(string)
131 c.Check(ok, Equals, true)173 c.Check(ok, Equals, true)
@@ -133,115 +175,103 @@
133 c.Check(err, IsNil)175 c.Check(err, IsNil)
134}176}
135177
136//178func (ss *serviceSuite) TestRegistrationFailsOnBadReqURL(c *C) {
137// Injection tests179 svc := NewPushService(ss.bus, ss.log)
138180 svc.SetRegistrationURL("%gh")
139func (ss *serviceSuite) TestInjectWorks(c *C) {181 reg, err := svc.register("thing", nil, nil)
140 svc := NewService(ss.bus, ss.log)182 c.Check(reg, IsNil)
141 rvs, err := svc.inject([]interface{}{"hello", "world"}, nil)183 c.Check(err, ErrorMatches, "unable to build register request: .*")
142 c.Assert(err, IsNil)184}
143 c.Check(rvs, IsNil)185
144 rvs, err = svc.inject([]interface{}{"hello", "there"}, nil)186func (ss *serviceSuite) TestRegistrationFailsOnBadAuth(c *C) {
145 c.Assert(err, IsNil)187 svc := NewPushService(ss.bus, ss.log)
146 c.Check(rvs, IsNil)188 // ... no auth added
147 c.Assert(svc.mbox, HasLen, 1)189 reg, err := svc.register("thing", nil, nil)
148 c.Assert(svc.mbox["hello"], HasLen, 2)190 c.Check(reg, IsNil)
149 c.Check(svc.mbox["hello"][0], Equals, "world")191 c.Check(err, Equals, BadAuth)
150 c.Check(svc.mbox["hello"][1], Equals, "there")192}
151193
152 // and check it fired the right signal (twice)194func (ss *serviceSuite) TestRegistrationFailsOnNoServer(c *C) {
153 callArgs := testibus.GetCallArgs(ss.bus)195 svc := NewPushService(ss.bus, ss.log)
154 c.Assert(callArgs, HasLen, 2)196 svc.SetRegistrationURL("xyzzy://")
155 c.Check(callArgs[0].Member, Equals, "::Signal")197 svc.SetAuthGetter(func(string) string { return "tok" })
156 c.Check(callArgs[0].Args, DeepEquals, []interface{}{"Notification", []interface{}{"hello"}})198 reg, err := svc.register("thing", nil, nil)
157 c.Check(callArgs[1], DeepEquals, callArgs[0])199 c.Check(reg, IsNil)
158}200 c.Check(err, ErrorMatches, "unable to request registration: .*")
159201}
160func (ss *serviceSuite) TestInjectFailsIfInjectFails(c *C) {202
161 bus := testibus.NewTestingEndpoint(condition.Work(true),203func (ss *serviceSuite) TestRegistrationFailsOn40x(c *C) {
162 condition.Work(false))204 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
163 svc := NewService(bus, ss.log)205 http.Error(w, "I'm a teapot", 418)
164 svc.SetMessageHandler(func([]byte) error { return errors.New("fail") })206 }))
165 _, err := svc.inject([]interface{}{"hello", "xyzzy"}, nil)207 defer ts.Close()
166 c.Check(err, NotNil)208
167}209 svc := NewPushService(ss.bus, ss.log)
168210 svc.SetAuthGetter(func(string) string { return "tok" })
169func (ss *serviceSuite) TestInjectFailsIfBadArgs(c *C) {211 svc.SetRegistrationURL(ts.URL)
170 for i, s := range []struct {212 reg, err := svc.register("/thing", nil, nil)
171 args []interface{}213 c.Check(err, Equals, BadRequest)
172 errt error214 c.Check(reg, IsNil)
173 }{215}
174 {nil, BadArgCount},216
175 {[]interface{}{}, BadArgCount},217func (ss *serviceSuite) TestRegistrationFailsOn50x(c *C) {
176 {[]interface{}{1}, BadArgCount},218 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
177 {[]interface{}{1, 2}, BadArgType},219 http.Error(w, "Not implemented", 501)
178 {[]interface{}{"1", 2}, BadArgType},220 }))
179 {[]interface{}{1, "2"}, BadArgType},221 defer ts.Close()
180 {[]interface{}{1, 2, 3}, BadArgCount},222
181 } {223 svc := NewPushService(ss.bus, ss.log)
182 reg, err := new(Service).inject(s.args, nil)224 svc.SetAuthGetter(func(string) string { return "tok" })
183 c.Check(reg, IsNil, Commentf("iteration #%d", i))225 svc.SetRegistrationURL(ts.URL)
184 c.Check(err, Equals, s.errt, Commentf("iteration #%d", i))226 reg, err := svc.register("/thing", nil, nil)
185 }227 c.Check(err, Equals, BadServer)
186}228 c.Check(reg, IsNil)
187229}
188//230
189// Notifications tests231func (ss *serviceSuite) TestRegistrationFailsOnBadJSON(c *C) {
190func (ss *serviceSuite) TestNotificationsWorks(c *C) {232 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
191 svc := NewService(ss.bus, ss.log)233 buf := make([]byte, 256)
192 nots, err := svc.notifications([]interface{}{"hello"}, nil)234 n, e := r.Body.Read(buf)
193 c.Assert(err, IsNil)235 c.Assert(e, IsNil)
194 c.Assert(nots, NotNil)236 req := registrationRequest{}
195 c.Assert(nots, HasLen, 1)237 c.Assert(json.Unmarshal(buf[:n], &req), IsNil)
196 c.Check(nots[0], HasLen, 0)238 c.Check(req, DeepEquals, registrationRequest{"fake-device-id", "an-app-id"})
197 if svc.mbox == nil {239
198 svc.mbox = make(map[string][]string)240 w.Header().Set("Content-Type", "application/json")
199 }241 fmt.Fprintln(w, `{`)
200 svc.mbox["hello"] = append(svc.mbox["hello"], "this", "thing")242 }))
201 nots, err = svc.notifications([]interface{}{"hello"}, nil)243 defer ts.Close()
202 c.Assert(err, IsNil)244
203 c.Assert(nots, NotNil)245 svc := NewPushService(ss.bus, ss.log)
204 c.Assert(nots, HasLen, 1)246 svc.SetAuthGetter(func(string) string { return "tok" })
205 c.Check(nots[0], DeepEquals, []string{"this", "thing"})247 svc.SetRegistrationURL(ts.URL)
206}248 svc.SetDeviceId("fake-device-id")
207249 // this'll check (un)quoting, too
208func (ss *serviceSuite) TestNotificationsFailsIfBadArgs(c *C) {250 reg, err := svc.register("/an_2dapp_2did", nil, nil)
209 for i, s := range []struct {251 c.Check(reg, IsNil)
210 args []interface{}252 c.Check(err, ErrorMatches, "unable to unmarshal register response: .*")
211 errt error253}
212 }{254
213 {nil, BadArgCount}, // no args255func (ss *serviceSuite) TestRegistrationFailsOnBadJSONDocument(c *C) {
214 {[]interface{}{}, BadArgCount}, // still no args256 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
215 {[]interface{}{42}, BadArgType}, // bad arg type257 buf := make([]byte, 256)
216 {[]interface{}{1, 2}, BadArgCount}, // too many args258 n, e := r.Body.Read(buf)
217 } {259 c.Assert(e, IsNil)
218 reg, err := new(Service).notifications(s.args, nil)260 req := registrationRequest{}
219 c.Check(reg, IsNil, Commentf("iteration #%d", i))261 c.Assert(json.Unmarshal(buf[:n], &req), IsNil)
220 c.Check(err, Equals, s.errt, Commentf("iteration #%d", i))262 c.Check(req, DeepEquals, registrationRequest{"fake-device-id", "an-app-id"})
221 }263
222}264 w.Header().Set("Content-Type", "application/json")
223265 fmt.Fprintln(w, `{"bananas": "very yes"}`)
224func (ss *serviceSuite) TestMessageHandler(c *C) {266 }))
225 svc := new(Service)267 defer ts.Close()
226 c.Assert(svc.msgHandler, IsNil)268
227 var ext = []byte{}269 svc := NewPushService(ss.bus, ss.log)
228 e := errors.New("Hello")270 svc.SetAuthGetter(func(string) string { return "tok" })
229 f := func(s []byte) error { ext = s; return e }271 svc.SetRegistrationURL(ts.URL)
230 c.Check(svc.GetMessageHandler(), IsNil)272 svc.SetDeviceId("fake-device-id")
231 svc.SetMessageHandler(f)273 // this'll check (un)quoting, too
232 c.Check(svc.GetMessageHandler(), NotNil)274 reg, err := svc.register("/an_2dapp_2did", nil, nil)
233 c.Check(svc.msgHandler([]byte("37")), Equals, e)275 c.Check(reg, IsNil)
234 c.Check(ext, DeepEquals, []byte("37"))276 c.Check(err, Equals, BadToken)
235}
236
237func (ss *serviceSuite) TestInjectCallsMessageHandler(c *C) {
238 var ext = []byte{}
239 svc := NewService(ss.bus, ss.log)
240 f := func(s []byte) error { ext = s; return nil }
241 svc.SetMessageHandler(f)
242 c.Check(svc.Inject("stuff", "{}"), IsNil)
243 c.Check(ext, DeepEquals, []byte("{}"))
244 err := errors.New("ouch")
245 svc.SetMessageHandler(func([]byte) error { return err })
246 c.Check(svc.Inject("stuff", "{}"), Equals, err)
247}277}
248278
=== modified file 'client/session/session.go'
--- client/session/session.go 2014-05-23 05:59:38 +0000
+++ client/session/session.go 2014-07-02 13:12:36 +0000
@@ -26,7 +26,6 @@
26 "fmt"26 "fmt"
27 "math/rand"27 "math/rand"
28 "net"28 "net"
29 "os/exec"
30 "strings"29 "strings"
31 "sync"30 "sync"
32 "sync/atomic"31 "sync/atomic"
@@ -87,7 +86,8 @@
87 ExpectAllRepairedTime time.Duration86 ExpectAllRepairedTime time.Duration
88 PEM []byte87 PEM []byte
89 Info map[string]interface{}88 Info map[string]interface{}
90 AuthHelper []string89 AuthGetter func(string) string
90 AuthURL string
91}91}
9292
93// ClientSession holds a client<->server session and its configuration.93// ClientSession holds a client<->server session and its configuration.
@@ -244,21 +244,10 @@
244// addAuthorization gets the authorization blob to send to the server244// addAuthorization gets the authorization blob to send to the server
245// and adds it to the session.245// and adds it to the session.
246func (sess *ClientSession) addAuthorization() error {246func (sess *ClientSession) addAuthorization() error {
247 sess.Log.Debugf("adding authorization")247 if sess.AuthGetter != nil {
248 // using a helper, for now at least248 sess.Log.Debugf("adding authorization")
249 if len(sess.AuthHelper) == 0 {249 sess.auth = sess.AuthGetter(sess.AuthURL)
250 // do nothing if helper is unset or empty250 }
251 return nil
252 }
253
254 auth, err := exec.Command(sess.AuthHelper[0], sess.AuthHelper[1:]...).Output()
255 if err != nil {
256 // For now we just log the error, as we don't want to block unauthorized users
257 sess.Log.Errorf("unable to get the authorization token from the account: %v", err)
258 } else {
259 sess.auth = strings.TrimSpace(string(auth))
260 }
261
262 return nil251 return nil
263}252}
264253
265254
=== modified file 'client/session/session_test.go'
--- client/session/session_test.go 2014-05-23 05:59:38 +0000
+++ client/session/session_test.go 2014-07-02 13:12:36 +0000
@@ -353,34 +353,21 @@
353****************************************************************/353****************************************************************/
354354
355func (cs *clientSessionSuite) TestAddAuthorizationAddsAuthorization(c *C) {355func (cs *clientSessionSuite) TestAddAuthorizationAddsAuthorization(c *C) {
356 sess := &ClientSession{Log: cs.log}356 url := "xyzzy://"
357 sess.AuthHelper = []string{"echo", "some auth"}357 sess := &ClientSession{Log: cs.log}
358 c.Assert(sess.auth, Equals, "")358 sess.AuthGetter = func(url string) string { return url + " auth'ed" }
359 err := sess.addAuthorization()359 sess.AuthURL = url
360 c.Assert(err, IsNil)360 c.Assert(sess.auth, Equals, "")
361 c.Check(sess.auth, Equals, "some auth")361 err := sess.addAuthorization()
362}362 c.Assert(err, IsNil)
363363 c.Check(sess.auth, Equals, "xyzzy:// auth'ed")
364func (cs *clientSessionSuite) TestAddAuthorizationIgnoresErrors(c *C) {364}
365 sess := &ClientSession{Log: cs.log}365
366 sess.AuthHelper = []string{"sh", "-c", "echo hello; false"}366func (cs *clientSessionSuite) TestAddAuthorizationSkipsIfUnset(c *C) {
367367 sess := &ClientSession{Log: cs.log}
368 c.Assert(sess.auth, Equals, "")368 sess.AuthGetter = nil
369 err := sess.addAuthorization()369 c.Assert(sess.auth, Equals, "")
370 c.Assert(err, IsNil)370 err := sess.addAuthorization()
371 c.Check(sess.auth, Equals, "")
372}
373
374func (cs *clientSessionSuite) TestAddAuthorizationSkipsIfUnsetOrNil(c *C) {
375 sess := &ClientSession{Log: cs.log}
376 sess.AuthHelper = nil
377 c.Assert(sess.auth, Equals, "")
378 err := sess.addAuthorization()
379 c.Assert(err, IsNil)
380 c.Check(sess.auth, Equals, "")
381
382 sess.AuthHelper = []string{}
383 err = sess.addAuthorization()
384 c.Assert(err, IsNil)371 c.Assert(err, IsNil)
385 c.Check(sess.auth, Equals, "")372 c.Check(sess.auth, Equals, "")
386}373}
387374
=== modified file 'debian/changelog'
--- debian/changelog 2014-06-05 09:42:22 +0000
+++ debian/changelog 2014-07-02 13:12:36 +0000
@@ -1,3 +1,37 @@
1ubuntu-push (0.42ubuntu1) UNRELEASED; urgency=medium
2
3 [ Samuele Pedroni ]
4 * Support registering tokens and sending notifications with a token
5 * Register script and scripts unicast support
6 * Update http13client from the actual go1.3 release
7 * Avoid late pings in the face of nop exchanges
8 * murmur3 upstream change of seed to 0
9
10 [ Roberto Alsina ]
11 * Make signing-helper generate a HTTP header instead of a querystring,
12 and take a URL to sign.
13 * Wrap libmessaging-menu to allow for persistent notifications.
14 * Wrap ubuntu-app-launch start_helper / stop_helper functions.
15
16 [ John R. Lenton ]
17 * Switch dbus api to retrieve app name from dbus path.
18 * Move signing bits up from session to client, for reuse by service.
19 * Change AuthHelper to be a string; auth helper should now expect a
20 parameter (the url to sign). Added SessionURL to config.
21 * Adapt our whoopsie wrapper to whoopsie's now more correct behavior wrt
22 failing to get a mac address.
23 * Add registration_url to config; hook up auth bits and reg url to
24 client & service.
25 * Do an HTTP POST to registration_url on register.
26 * Fix debian/rules so packaging-time tests pass (ugh)
27 * Refactoring notification providers.
28 * Get the small messaging-menu wrapper working (thanks larsu & dednick)
29
30 [ Guillermo Gonzalez ]
31 * Split DBus service into PushService and PostalService
32
33 -- John R. Lenton <john.lenton@canonical.com> Wed, 02 Jul 2014 13:49:01 +0100
34
1ubuntu-push (0.3+14.10.20140605-0ubuntu1) utopic; urgency=medium35ubuntu-push (0.3+14.10.20140605-0ubuntu1) utopic; urgency=medium
236
3 [ John Lenton ]37 [ John Lenton ]
438
=== modified file 'debian/config.json'
--- debian/config.json 2014-06-04 12:18:42 +0000
+++ debian/config.json 2014-07-02 13:12:36 +0000
@@ -1,5 +1,7 @@
1{1{
2 "auth_helper": [],2 "auth_helper": "/usr/lib/ubuntu-push-client/signing-helper",
3 "session_url": "https://push.ubuntu.com/",
4 "registration_url": "https://push.ubuntu.com/register",
3 "connect_timeout": "20s",5 "connect_timeout": "20s",
4 "exchange_timeout": "30s",6 "exchange_timeout": "30s",
5 "hosts_cache_expiry": "12h",7 "hosts_cache_expiry": "12h",
68
=== modified file 'debian/control'
--- debian/control 2014-06-02 10:01:13 +0000
+++ debian/control 2014-07-02 13:12:36 +0000
@@ -14,7 +14,9 @@
14 golang-uuid-dev,14 golang-uuid-dev,
15 libgcrypt11-dev,15 libgcrypt11-dev,
16 libglib2.0-dev (>= 2.31.6),16 libglib2.0-dev (>= 2.31.6),
17 libmessaging-menu-dev,
17 libwhoopsie-dev,18 libwhoopsie-dev,
19 libubuntu-app-launch2-dev,
18 libubuntuoneauth-2.0-dev,20 libubuntuoneauth-2.0-dev,
19 libdbus-1-dev,21 libdbus-1-dev,
20 libnih-dbus-dev,22 libnih-dbus-dev,
2123
=== modified file 'debian/rules'
--- debian/rules 2014-05-02 12:42:27 +0000
+++ debian/rules 2014-07-02 13:12:36 +0000
@@ -5,10 +5,15 @@
5export UBUNTU_PUSH_TEST_RESOURCES_ROOT := $(CURDIR)5export UBUNTU_PUSH_TEST_RESOURCES_ROOT := $(CURDIR)
66
7override_dh_auto_build:7override_dh_auto_build:
8 cd $$( find ./ -type d -regex '\./[^/]*/src/launchpad.net' -printf "%h\n" | head -n1)
9 dh_auto_build --buildsystem=golang8 dh_auto_build --buildsystem=golang
10 (cd signing-helper && cmake . && make)9 (cd signing-helper && cmake . && make)
1110
11# overriding dh_auto_test because the http13client tests don't all pass on go < 1.3
12# (should go away once we ship go 1.3)
13override_dh_auto_test:
14 cd $$( find ./ -type d -regex '\./[^/]*/src/launchpad.net' -printf "%h\n" | head -n1) && \
15 env GOPATH=$$(cd ..; pwd) go test $$(env GOPATH=$$(cd ..; pwd) go list $(DH_GOPKG)/... | grep -v acceptance | grep -v http13client )
16
12override_dh_install:17override_dh_install:
13 dh_install -Xusr/bin/cmd -Xusr/bin/dev --fail-missing18 dh_install -Xusr/bin/cmd -Xusr/bin/dev --fail-missing
1419
1520
=== modified file 'external/README'
--- external/README 2014-03-24 15:37:36 +0000
+++ external/README 2014-07-02 13:12:36 +0000
@@ -1,3 +1,3 @@
1Directly included vendorized small packages.1Directly included vendorized small packages.
22
3* murmor3 comes from import at lp:~ubuntu-push-hackers/ubuntu-push/murmur at revno 103* murmur3 comes from import at lp:~ubuntu-push-hackers/ubuntu-push/murmur at revno 12
44
=== modified file 'external/murmur3/murmur128.go'
--- external/murmur3/murmur128.go 2014-03-24 15:31:42 +0000
+++ external/murmur3/murmur128.go 2014-07-02 13:12:36 +0000
@@ -40,7 +40,7 @@
4040
41func (d *digest128) Size() int { return 16 }41func (d *digest128) Size() int { return 16 }
4242
43func (d *digest128) reset() { d.h1, d.h2 = 1, 1 }43func (d *digest128) reset() { d.h1, d.h2 = 0, 0 }
4444
45func (d *digest128) Sum(b []byte) []byte {45func (d *digest128) Sum(b []byte) []byte {
46 h1, h2 := d.h1, d.h246 h1, h2 := d.h1, d.h2
@@ -182,7 +182,7 @@
182// hasher.Write(data)182// hasher.Write(data)
183// return hasher.Sum128()183// return hasher.Sum128()
184func Sum128(data []byte) (h1 uint64, h2 uint64) {184func Sum128(data []byte) (h1 uint64, h2 uint64) {
185 d := &digest128{h1: 1, h2: 1}185 d := &digest128{h1: 0, h2: 0}
186 d.tail = d.bmix(data)186 d.tail = d.bmix(data)
187 d.clen = len(data)187 d.clen = len(data)
188 return d.Sum128()188 return d.Sum128()
189189
=== modified file 'external/murmur3/murmur32.go'
--- external/murmur3/murmur32.go 2014-03-24 15:31:42 +0000
+++ external/murmur3/murmur32.go 2014-07-02 13:12:36 +0000
@@ -33,7 +33,7 @@
3333
34func (d *digest32) Size() int { return 4 }34func (d *digest32) Size() int { return 4 }
3535
36func (d *digest32) reset() { d.h1 = 1 }36func (d *digest32) reset() { d.h1 = 0 }
3737
38func (d *digest32) Sum(b []byte) []byte {38func (d *digest32) Sum(b []byte) []byte {
39 h := d.h139 h := d.h1
@@ -104,7 +104,7 @@
104// return hasher.Sum32()104// return hasher.Sum32()
105func Sum32(data []byte) uint32 {105func Sum32(data []byte) uint32 {
106106
107 var h1 uint32 = 1107 var h1 uint32 = 0
108108
109 nblocks := len(data) / 4109 nblocks := len(data) / 4
110 var p uintptr110 var p uintptr
111111
=== modified file 'external/murmur3/murmur64.go'
--- external/murmur3/murmur64.go 2014-03-24 15:31:42 +0000
+++ external/murmur3/murmur64.go 2014-07-02 13:12:36 +0000
@@ -37,7 +37,7 @@
37// hasher.Write(data)37// hasher.Write(data)
38// return hasher.Sum64()38// return hasher.Sum64()
39func Sum64(data []byte) uint64 {39func Sum64(data []byte) uint64 {
40 d := &digest128{h1: 1, h2: 1}40 d := &digest128{h1: 0, h2: 0}
41 d.tail = d.bmix(data)41 d.tail = d.bmix(data)
42 d.clen = len(data)42 d.clen = len(data)
43 h1, _ := d.Sum128()43 h1, _ := d.Sum128()
4444
=== modified file 'external/murmur3/murmur_test.go'
--- external/murmur3/murmur_test.go 2014-03-24 15:31:42 +0000
+++ external/murmur3/murmur_test.go 2014-07-02 13:12:36 +0000
@@ -11,11 +11,11 @@
11 h64_2 uint6411 h64_2 uint64
12 s string12 s string
13}{13}{
14 {0x514e28b7, 0x4610abe56eff5cb5, 0x51622daa78f83583, ""},14 {0x00000000, 0x0000000000000000, 0x0000000000000000, ""},
15 {0xbb4abcad, 0xa78ddff5adae8d10, 0x128900ef20900135, "hello"},15 {0x248bfa47, 0xcbd8a7b341bd9b02, 0x5b1e906a48ae1d19, "hello"},
16 {0x6f5cb2e9, 0x8b95f808840725c6, 0x1597ed5422bd493b, "hello, world"},16 {0x149bbb7f, 0x342fac623a5ebc8e, 0x4cdcbc079642414d, "hello, world"},
17 {0xf50e1f30, 0x2a929de9c8f97b2f, 0x56a41d99af43a2db, "19 Jan 2038 at 3:14:07 AM"},17 {0xe31e8a70, 0xb89e5988b737affc, 0x664fc2950231b2cb, "19 Jan 2038 at 3:14:07 AM"},
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."},
19}19}
2020
21func TestRef(t *testing.T) {21func TestRef(t *testing.T) {
@@ -37,6 +37,10 @@
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)
38 }38 }
3939
40 if v := Sum64([]byte(elem.s)); v != elem.h64_1 {
41 t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h64_1)
42 }
43
40 var h128 Hash128 = New128()44 var h128 Hash128 = New128()
41 h128.Write([]byte(elem.s))45 h128.Write([]byte(elem.s))
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 {
4347
=== modified file 'http13client/Makefile'
--- http13client/Makefile 2014-03-20 12:15:36 +0000
+++ http13client/Makefile 2014-07-02 13:12:36 +0000
@@ -20,7 +20,8 @@
2020
21prune:21prune:
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 \
23 sniff*.go httptest httputil testdata triv.go jar.go status.go23 sniff*.go httptest httputil testdata triv.go jar.go status.go \
24 cookie_test.go
24 sed -i -e 's+"launchpad.net/ubuntu-push/http13client/+"net/http/+' *.go25 sed -i -e 's+"launchpad.net/ubuntu-push/http13client/+"net/http/+' *.go
2526
26fix:27fix:
2728
=== modified file 'http13client/_patches/empty_server.patch'
--- http13client/_patches/empty_server.patch 2014-03-19 23:13:58 +0000
+++ http13client/_patches/empty_server.patch 2014-07-02 13:12:36 +0000
@@ -1,6 +1,6 @@
1=== modified file 'http13client/serve_test.go'1=== modified file 'http13client/serve_test.go'
2--- http13client/serve_test.go 2014-03-19 21:38:56 +00002--- http13client/serve_test.go 2014-06-20 11:00:47 +0000
3+++ http13client/serve_test.go 2014-03-19 22:27:37 +00003+++ http13client/serve_test.go 2014-06-20 12:00:22 +0000
4@@ -2,60 +2,15 @@4@@ -2,60 +2,15 @@
5 // Use of this source code is governed by a BSD-style5 // Use of this source code is governed by a BSD-style
6 // license that can be found in the LICENSE file.6 // license that can be found in the LICENSE file.
@@ -62,7 +62,7 @@
62 62
63 func (a dummyAddr) Network() string {63 func (a dummyAddr) Network() string {
64 return string(a)64 return string(a)
65@@ -93,1289 +48,6 @@65@@ -93,1325 +48,6 @@
66 return nil66 return nil
67 }67 }
68 68
@@ -906,31 +906,50 @@
906-}906-}
907-907-
908-type serverExpectTest struct {908-type serverExpectTest struct {
909- contentLength int // of request body909- contentLength int // of request body
910- chunked bool
910- expectation string // e.g. "100-continue"911- expectation string // e.g. "100-continue"
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)
912- expectedResponse string // expected substring in first line of http response913- expectedResponse string // expected substring in first line of http response
913-}914-}
914-915-
916-func expectTest(contentLength int, expectation string, readBody bool, expectedResponse string) serverExpectTest {
917- return serverExpectTest{
918- contentLength: contentLength,
919- expectation: expectation,
920- readBody: readBody,
921- expectedResponse: expectedResponse,
922- }
923-}
924-
915-var serverExpectTests = []serverExpectTest{925-var serverExpectTests = []serverExpectTest{
916- // Normal 100-continues, case-insensitive.926- // Normal 100-continues, case-insensitive.
917- {100, "100-continue", true, "100 Continue"},927- expectTest(100, "100-continue", true, "100 Continue"),
918- {100, "100-cOntInUE", true, "100 Continue"},928- expectTest(100, "100-cOntInUE", true, "100 Continue"),
919-929-
920- // No 100-continue.930- // No 100-continue.
921- {100, "", true, "200 OK"},931- expectTest(100, "", true, "200 OK"),
922-932-
923- // 100-continue but requesting client to deny us,933- // 100-continue but requesting client to deny us,
924- // so it never reads the body.934- // so it never reads the body.
925- {100, "100-continue", false, "401 Unauthorized"},935- expectTest(100, "100-continue", false, "401 Unauthorized"),
926- // Likewise without 100-continue:936- // Likewise without 100-continue:
927- {100, "", false, "401 Unauthorized"},937- expectTest(100, "", false, "401 Unauthorized"),
928-938-
929- // Non-standard expectations are failures939- // Non-standard expectations are failures
930- {0, "a-pony", false, "417 Expectation Failed"},940- expectTest(0, "a-pony", false, "417 Expectation Failed"),
931-941-
932- // Expect-100 requested but no body942- // Expect-100 requested but no body (is apparently okay: Issue 7625)
933- {0, "100-continue", true, "400 Bad Request"},943- expectTest(0, "100-continue", true, "200 OK"),
944- // Expect-100 requested but handler doesn't read the body
945- expectTest(0, "100-continue", false, "401 Unauthorized"),
946- // Expect-100 continue with no body, but a chunked body.
947- {
948- expectation: "100-continue",
949- readBody: true,
950- chunked: true,
951- expectedResponse: "100 Continue",
952- },
934-}953-}
935-954-
936-// Tests that the server responds to the "Expect" request header955-// Tests that the server responds to the "Expect" request header
@@ -959,21 +978,38 @@
959-978-
960- // Only send the body immediately if we're acting like an HTTP client979- // Only send the body immediately if we're acting like an HTTP client
961- // that doesn't send 100-continue expectations.980- // that doesn't send 100-continue expectations.
962- writeBody := test.contentLength > 0 && strings.ToLower(test.expectation) != "100-continue"981- writeBody := test.contentLength != 0 && strings.ToLower(test.expectation) != "100-continue"
963-982-
964- go func() {983- go func() {
984- contentLen := fmt.Sprintf("Content-Length: %d", test.contentLength)
985- if test.chunked {
986- contentLen = "Transfer-Encoding: chunked"
987- }
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"+
966- "Connection: close\r\n"+989- "Connection: close\r\n"+
967- "Content-Length: %d\r\n"+990- "%s\r\n"+
968- "Expect: %s\r\nHost: foo\r\n\r\n",991- "Expect: %s\r\nHost: foo\r\n\r\n",
969- test.readBody, test.contentLength, test.expectation)992- test.readBody, contentLen, test.expectation)
970- if err != nil {993- if err != nil {
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)
972- return995- return
973- }996- }
974- if writeBody {997- if writeBody {
998- var targ io.WriteCloser = struct {
999- io.Writer
1000- io.Closer
1001- }{
1002- conn,
1003- ioutil.NopCloser(nil),
1004- }
1005- if test.chunked {
1006- targ = httputil.NewChunkedWriter(conn)
1007- }
975- body := strings.Repeat("A", test.contentLength)1008- body := strings.Repeat("A", test.contentLength)
976- _, err = fmt.Fprint(conn, body)1009- _, err = fmt.Fprint(targ, body)
1010- if err == nil {
1011- err = targ.Close()
1012- }
977- if err != nil {1013- if err != nil {
978- if !test.readBody {1014- if !test.readBody {
979- // Server likely already hung up on us.1015- // Server likely already hung up on us.
@@ -1352,7 +1388,7 @@
1352 type neverEnding byte1388 type neverEnding byte
1353 1389
1354 func (b neverEnding) Read(p []byte) (n int, err error) {1390 func (b neverEnding) Read(p []byte) (n int, err error) {
1355@@ -1384,1344 +56,3 @@1391@@ -1420,1392 +56,3 @@
1356 }1392 }
1357 return len(p), nil1393 return len(p), nil
1358 }1394 }
@@ -2080,7 +2116,7 @@
2080- got := ht.rawResponse(req)2116- got := ht.rawResponse(req)
2081- wantStatus := fmt.Sprintf("%d %s", code, StatusText(code))2117- wantStatus := fmt.Sprintf("%d %s", code, StatusText(code))
2082- if !strings.Contains(got, wantStatus) {2118- if !strings.Contains(got, wantStatus) {
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)
2084- } else if strings.Contains(got, "Content-Length") {2120- } else if strings.Contains(got, "Content-Length") {
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)
2086- } else if strings.Contains(got, "stuff") {2122- } else if strings.Contains(got, "stuff") {
@@ -2090,6 +2126,21 @@
2090- }2126- }
2091-}2127-}
2092-2128-
2129-func TestContentTypeOkayOn204(t *testing.T) {
2130- ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) {
2131- w.Header().Set("Content-Length", "123") // suppressed
2132- w.Header().Set("Content-Type", "foo/bar")
2133- w.WriteHeader(204)
2134- }))
2135- got := ht.rawResponse("GET / HTTP/1.1")
2136- if !strings.Contains(got, "Content-Type: foo/bar") {
2137- t.Errorf("Response = %q; want Content-Type: foo/bar", got)
2138- }
2139- if strings.Contains(got, "Content-Length: 123") {
2140- t.Errorf("Response = %q; don't want a Content-Length", got)
2141- }
2142-}
2143-
2093-// Issue 69952144-// Issue 6995
2094-// A server Handler can receive a Request, and then turn around and2145-// A server Handler can receive a Request, and then turn around and
2095-// give a copy of that Request.Body out to the Transport (e.g. any2146-// give a copy of that Request.Body out to the Transport (e.g. any
@@ -2261,7 +2312,7 @@
2261- ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0)2312- ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0)
2262- ts.Config.ConnState = func(c net.Conn, state ConnState) {2313- ts.Config.ConnState = func(c net.Conn, state ConnState) {
2263- if c == nil {2314- if c == nil {
2264- t.Error("nil conn seen in state %s", state)2315- t.Errorf("nil conn seen in state %s", state)
2265- return2316- return
2266- }2317- }
2267- mu.Lock()2318- mu.Lock()
@@ -2397,6 +2448,39 @@
2397- }2448- }
2398-}2449-}
2399-2450-
2451-// golang.org/issue/7856
2452-func TestServerEmptyBodyRace(t *testing.T) {
2453- defer afterTest(t)
2454- var n int32
2455- ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
2456- atomic.AddInt32(&n, 1)
2457- }))
2458- defer ts.Close()
2459- var wg sync.WaitGroup
2460- const reqs = 20
2461- for i := 0; i < reqs; i++ {
2462- wg.Add(1)
2463- go func() {
2464- defer wg.Done()
2465- res, err := Get(ts.URL)
2466- if err != nil {
2467- t.Error(err)
2468- return
2469- }
2470- defer res.Body.Close()
2471- _, err = io.Copy(ioutil.Discard, res.Body)
2472- if err != nil {
2473- t.Error(err)
2474- return
2475- }
2476- }()
2477- }
2478- wg.Wait()
2479- if got := atomic.LoadInt32(&n); got != reqs {
2480- t.Errorf("handler ran %d times; want %d", got, reqs)
2481- }
2482-}
2483-
2400-func TestServerConnStateNew(t *testing.T) {2484-func TestServerConnStateNew(t *testing.T) {
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.
2402- srv := &Server{2486- srv := &Server{
@@ -2699,9 +2783,9 @@
2699-}2783-}
27002784
2701=== modified file 'http13client/server.go'2785=== modified file 'http13client/server.go'
2702--- http13client/server.go 2014-03-19 20:20:19 +00002786--- http13client/server.go 2014-06-20 11:00:47 +0000
2703+++ http13client/server.go 2014-03-19 22:27:37 +00002787+++ http13client/server.go 2014-06-20 12:05:53 +0000
2704@@ -2,1984 +2,17 @@2788@@ -2,1976 +2,16 @@
2705 // Use of this source code is governed by a BSD-style2789 // Use of this source code is governed by a BSD-style
2706 // license that can be found in the LICENSE file.2790 // license that can be found in the LICENSE file.
2707 2791
@@ -2723,7 +2807,7 @@
2723- "path"2807- "path"
2724- "runtime"2808- "runtime"
2725- "strconv"2809- "strconv"
2726 "strings"2810- "strings"
2727 "sync"2811 "sync"
2728- "sync/atomic"2812- "sync/atomic"
2729- "time"2813- "time"
@@ -3506,18 +3590,16 @@
3506- }3590- }
3507-3591-
3508- code := w.status3592- code := w.status
3509- if !bodyAllowedForStatus(code) {3593- if bodyAllowedForStatus(code) {
3510- // Must not have body.
3511- // RFC 2616 section 10.3.5: "the response MUST NOT include other entity-headers"
3512- for _, k := range []string{"Content-Type", "Content-Length", "Transfer-Encoding"} {
3513- delHeader(k)
3514- }
3515- } else {
3516- // If no content type, apply sniffing algorithm to body.3594- // If no content type, apply sniffing algorithm to body.
3517- _, haveType := header["Content-Type"]3595- _, haveType := header["Content-Type"]
3518- if !haveType {3596- if !haveType {
3519- setHeader.contentType = DetectContentType(p)3597- setHeader.contentType = DetectContentType(p)
3520- }3598- }
3599- } else {
3600- for _, k := range suppressedHeaders(code) {
3601- delHeader(k)
3602- }
3521- }3603- }
3522-3604-
3523- if _, ok := header["Date"]; !ok {3605- if _, ok := header["Date"]; !ok {
@@ -3865,16 +3947,10 @@
3865- // Expect 100 Continue support3947- // Expect 100 Continue support
3866- req := w.req3948- req := w.req
3867- if req.expectsContinue() {3949- if req.expectsContinue() {
3868- if req.ProtoAtLeast(1, 1) {3950- if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
3869- // Wrap the Body reader with one that replies on the connection3951- // Wrap the Body reader with one that replies on the connection
3870- req.Body = &expectContinueReader{readCloser: req.Body, resp: w}3952- req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
3871- }3953- }
3872- if req.ContentLength == 0 {
3873- w.Header().Set("Connection", "close")
3874- w.WriteHeader(StatusBadRequest)
3875- w.finishRequest()
3876- break
3877- }
3878- req.Header.Del("Expect")3954- req.Header.Del("Expect")
3879- } else if req.Header.get("Expect") != "" {3955- } else if req.Header.get("Expect") != "" {
3880- w.sendExpectationFailed()3956- w.sendExpectationFailed()
@@ -4685,11 +4761,11 @@
4685-}4761-}
4686+)4762+)
4687 4763
4688 // eofReader is a non-nil io.ReadCloser that always returns EOF.4764 type eofReaderWithWriteTo struct{}
4689 // It embeds a *strings.Reader so it still has a WriteTo method4765
4690@@ -1992,28 +25,6 @@4766@@ -1991,28 +31,6 @@
4691 ioutil.NopCloser(nil),4767 // Verify that an io.Copy from an eofReader won't require a buffer.
4692 }4768 var _ io.WriterTo = eofReader
4693 4769
4694-// initNPNRequest is an HTTP handler that initializes certain4770-// initNPNRequest is an HTTP handler that initializes certain
4695-// uninitialized fields in its *Request. Such partially-initialized4771-// uninitialized fields in its *Request. Such partially-initialized
46964772
=== modified file 'http13client/_patches/fix_code.patch'
--- http13client/_patches/fix_code.patch 2014-03-19 23:13:58 +0000
+++ http13client/_patches/fix_code.patch 2014-07-02 13:12:36 +0000
@@ -1,6 +1,6 @@
1=== modified file 'http13client/client.go'1=== modified file 'http13client/client.go'
2--- http13client/client.go 2014-03-19 20:20:19 +00002--- http13client/client.go 2014-06-20 11:00:47 +0000
3+++ http13client/client.go 2014-03-19 22:27:37 +00003+++ http13client/client.go 2014-06-20 12:05:53 +0000
4@@ -17,6 +17,7 @@4@@ -17,6 +17,7 @@
5 "io/ioutil"5 "io/ioutil"
6 "log"6 "log"
@@ -18,7 +18,7 @@
18 18
19 // Timeout specifies a time limit for requests made by this19 // Timeout specifies a time limit for requests made by this
20 // Client. The timeout includes connection time, any20 // Client. The timeout includes connection time, any
21@@ -177,7 +178,7 @@21@@ -184,7 +185,7 @@
22 // Headers, leaving it uninitialized. We guarantee to the22 // Headers, leaving it uninitialized. We guarantee to the
23 // Transport that this has been initialized, though.23 // Transport that this has been initialized, though.
24 if req.Header == nil {24 if req.Header == nil {
@@ -27,7 +27,7 @@
27 }27 }
28 28
29 if u := req.URL.User; u != nil {29 if u := req.URL.User; u != nil {
30@@ -308,7 +309,7 @@30@@ -316,7 +317,7 @@
31 if ireq.Method == "POST" || ireq.Method == "PUT" {31 if ireq.Method == "POST" || ireq.Method == "PUT" {
32 nreq.Method = "GET"32 nreq.Method = "GET"
33 }33 }
@@ -38,8 +38,8 @@
38 break38 break
3939
40=== modified file 'http13client/cookie.go'40=== modified file 'http13client/cookie.go'
41--- http13client/cookie.go 2014-03-19 20:20:19 +000041--- http13client/cookie.go 2014-06-20 11:00:47 +0000
42+++ http13client/cookie.go 2014-03-19 22:27:37 +000042+++ http13client/cookie.go 2014-06-20 12:05:53 +0000
43@@ -5,10 +5,9 @@43@@ -5,10 +5,9 @@
44 package http44 package http
45 45
@@ -94,7 +94,7 @@
94 Name: name,94 Name: name,
95 Value: value,95 Value: value,
96 Raw: line,96 Raw: line,
97@@ -129,59 +108,12 @@97@@ -125,59 +104,12 @@
98 return cookies98 return cookies
99 }99 }
100 100
@@ -156,7 +156,7 @@
156 lines, ok := h["Cookie"]156 lines, ok := h["Cookie"]
157 if !ok {157 if !ok {
158 return cookies158 return cookies
159@@ -213,7 +145,7 @@159@@ -209,7 +141,7 @@
160 if !success {160 if !success {
161 continue161 continue
162 }162 }
@@ -167,8 +167,8 @@
167 }167 }
168168
169=== modified file 'http13client/header.go'169=== modified file 'http13client/header.go'
170--- http13client/header.go 2014-03-19 20:20:19 +0000170--- http13client/header.go 2014-06-20 11:00:47 +0000
171+++ http13client/header.go 2014-03-19 22:27:37 +0000171+++ http13client/header.go 2014-06-20 12:00:22 +0000
172@@ -5,176 +5,9 @@172@@ -5,176 +5,9 @@
173 package http173 package http
174 174
@@ -348,8 +348,8 @@
348 // token must be all lowercase.348 // token must be all lowercase.
349349
350=== modified file 'http13client/request.go'350=== modified file 'http13client/request.go'
351--- http13client/request.go 2014-03-19 20:20:19 +0000351--- http13client/request.go 2014-06-20 11:00:47 +0000
352+++ http13client/request.go 2014-03-19 22:27:37 +0000352+++ http13client/request.go 2014-06-20 12:05:53 +0000
353@@ -16,6 +16,7 @@353@@ -16,6 +16,7 @@
354 "io/ioutil"354 "io/ioutil"
355 "mime"355 "mime"
@@ -358,25 +358,25 @@
358 "net/textproto"358 "net/textproto"
359 "net/url"359 "net/url"
360 "strconv"360 "strconv"
361@@ -103,7 +104,7 @@361@@ -121,7 +122,7 @@
362 // The request parser implements this by canonicalizing the362 // added and may override values in Header.
363 // name, making the first character and any characters363 //
364 // following a hyphen uppercase and the rest lowercase.364 // See the documentation for the Request.Write method.
365- Header Header365- Header Header
366+ Header http.Header366+ Header http.Header
367 367
368 // Body is the request's body.368 // Body is the request's body.
369 //369 //
370@@ -164,7 +165,7 @@370@@ -199,7 +200,7 @@
371 // For server requests, Trailer is only populated after Body has been371 // not mutate Trailer.
372 // closed or fully consumed.372 //
373 // Trailer support is only partially complete.373 // Few HTTP clients, servers, or proxies support HTTP trailers.
374- Trailer Header374- Trailer Header
375+ Trailer http.Header375+ Trailer http.Header
376 376
377 // RemoteAddr allows HTTP servers and other software to record377 // RemoteAddr allows HTTP servers and other software to record
378 // the network address that sent the request, usually for378 // the network address that sent the request, usually for
379@@ -204,7 +205,7 @@379@@ -239,7 +240,7 @@
380 }380 }
381 381
382 // Cookies parses and returns the HTTP cookies sent with the request.382 // Cookies parses and returns the HTTP cookies sent with the request.
@@ -385,7 +385,7 @@
385 return readCookies(r.Header, "")385 return readCookies(r.Header, "")
386 }386 }
387 387
388@@ -212,7 +213,7 @@388@@ -247,7 +248,7 @@
389 389
390 // Cookie returns the named cookie provided in the request or390 // Cookie returns the named cookie provided in the request or
391 // ErrNoCookie if not found.391 // ErrNoCookie if not found.
@@ -394,7 +394,7 @@
394 for _, c := range readCookies(r.Header, name) {394 for _, c := range readCookies(r.Header, name) {
395 return c, nil395 return c, nil
396 }396 }
397@@ -223,7 +224,7 @@397@@ -258,7 +259,7 @@
398 // AddCookie does not attach more than one Cookie header field. That398 // AddCookie does not attach more than one Cookie header field. That
399 // means all cookies, if any, are written into the same line,399 // means all cookies, if any, are written into the same line,
400 // separated by semicolon.400 // separated by semicolon.
@@ -403,7 +403,7 @@
403 s := fmt.Sprintf("%s=%s", sanitizeCookieName(c.Name), sanitizeCookieValue(c.Value))403 s := fmt.Sprintf("%s=%s", sanitizeCookieName(c.Name), sanitizeCookieValue(c.Value))
404 if c := r.Header.Get("Cookie"); c != "" {404 if c := r.Header.Get("Cookie"); c != "" {
405 r.Header.Set("Cookie", c+"; "+s)405 r.Header.Set("Cookie", c+"; "+s)
406@@ -326,7 +327,7 @@406@@ -361,7 +362,7 @@
407 }407 }
408 408
409 // extraHeaders may be nil409 // extraHeaders may be nil
@@ -412,7 +412,7 @@
412 host := req.Host412 host := req.Host
413 if host == "" {413 if host == "" {
414 if req.URL == nil {414 if req.URL == nil {
415@@ -456,7 +457,7 @@415@@ -490,7 +491,7 @@
416 Proto: "HTTP/1.1",416 Proto: "HTTP/1.1",
417 ProtoMajor: 1,417 ProtoMajor: 1,
418 ProtoMinor: 1,418 ProtoMinor: 1,
@@ -421,7 +421,7 @@
421 Body: rc,421 Body: rc,
422 Host: u.Host,422 Host: u.Host,
423 }423 }
424@@ -571,7 +572,7 @@424@@ -605,7 +606,7 @@
425 if err != nil {425 if err != nil {
426 return nil, err426 return nil, err
427 }427 }
@@ -430,7 +430,7 @@
430 430
431 // RFC2616: Must treat431 // RFC2616: Must treat
432 // GET /index.html HTTP/1.1432 // GET /index.html HTTP/1.1
433@@ -582,7 +583,7 @@433@@ -616,7 +617,7 @@
434 // the same. In the second case, any Host line is ignored.434 // the same. In the second case, any Host line is ignored.
435 req.Host = req.URL.Host435 req.Host = req.URL.Host
436 if req.Host == "" {436 if req.Host == "" {
@@ -439,7 +439,7 @@
439 }439 }
440 delete(req.Header, "Host")440 delete(req.Header, "Host")
441 441
442@@ -630,12 +631,12 @@442@@ -638,12 +639,12 @@
443 //443 //
444 // MaxBytesReader prevents clients from accidentally or maliciously444 // MaxBytesReader prevents clients from accidentally or maliciously
445 // sending a large request and wasting server resources.445 // sending a large request and wasting server resources.
@@ -454,7 +454,7 @@
454 r io.ReadCloser // underlying reader454 r io.ReadCloser // underlying reader
455 n int64 // max bytes remaining455 n int64 // max bytes remaining
456 stopped bool456 stopped bool
457@@ -645,9 +646,6 @@457@@ -653,9 +654,6 @@
458 if l.n <= 0 {458 if l.n <= 0 {
459 if !l.stopped {459 if !l.stopped {
460 l.stopped = true460 l.stopped = true
@@ -464,7 +464,7 @@
464 }464 }
465 return 0, errors.New("http: request body too large")465 return 0, errors.New("http: request body too large")
466 }466 }
467@@ -852,16 +850,16 @@467@@ -858,18 +856,18 @@
468 }468 }
469 469
470 func (r *Request) expectsContinue() bool {470 func (r *Request) expectsContinue() bool {
@@ -484,11 +484,13 @@
484- return hasToken(r.Header.get("Connection"), "close")484- return hasToken(r.Header.get("Connection"), "close")
485+ return hasToken(r.Header.Get("Connection"), "close")485+ return hasToken(r.Header.Get("Connection"), "close")
486 }486 }
487
488 func (r *Request) closeBody() {
487489
488=== modified file 'http13client/response.go'490=== modified file 'http13client/response.go'
489--- http13client/response.go 2014-03-19 20:20:19 +0000491--- http13client/response.go 2014-06-20 11:00:47 +0000
490+++ http13client/response.go 2014-03-19 22:27:37 +0000492+++ http13client/response.go 2014-06-20 12:05:53 +0000
491@@ -11,6 +11,7 @@493@@ -12,6 +12,7 @@
492 "crypto/tls"494 "crypto/tls"
493 "errors"495 "errors"
494 "io"496 "io"
@@ -496,7 +498,7 @@
496 "net/textproto"498 "net/textproto"
497 "net/url"499 "net/url"
498 "strconv"500 "strconv"
499@@ -40,7 +41,7 @@501@@ -41,7 +42,7 @@
500 // omitted from Header.502 // omitted from Header.
501 //503 //
502 // Keys in the map are canonicalized (see CanonicalHeaderKey).504 // Keys in the map are canonicalized (see CanonicalHeaderKey).
@@ -505,7 +507,7 @@
505 507
506 // Body represents the response body.508 // Body represents the response body.
507 //509 //
508@@ -69,7 +70,7 @@510@@ -71,7 +72,7 @@
509 511
510 // Trailer maps trailer keys to values, in the same512 // Trailer maps trailer keys to values, in the same
511 // format as the header.513 // format as the header.
@@ -514,7 +516,7 @@
514 516
515 // The Request that was sent to obtain this Response.517 // The Request that was sent to obtain this Response.
516 // Request's Body is nil (having already been consumed).518 // Request's Body is nil (having already been consumed).
517@@ -84,7 +85,7 @@519@@ -86,7 +87,7 @@
518 }520 }
519 521
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.
@@ -523,7 +525,7 @@
523 return readSetCookies(r.Header)525 return readSetCookies(r.Header)
524 }526 }
525 527
526@@ -153,7 +154,7 @@528@@ -155,7 +156,7 @@
527 }529 }
528 return nil, err530 return nil, err
529 }531 }
@@ -532,7 +534,7 @@
532 534
533 fixPragmaCacheControl(resp.Header)535 fixPragmaCacheControl(resp.Header)
534 536
535@@ -169,7 +170,7 @@537@@ -171,7 +172,7 @@
536 // Pragma: no-cache538 // Pragma: no-cache
537 // like539 // like
538 // Cache-Control: no-cache540 // Cache-Control: no-cache
@@ -543,17 +545,17 @@
543 header["Cache-Control"] = []string{"no-cache"}545 header["Cache-Control"] = []string{"no-cache"}
544546
545=== modified file 'http13client/transfer.go'547=== modified file 'http13client/transfer.go'
546--- http13client/transfer.go 2014-03-19 20:20:19 +0000548--- http13client/transfer.go 2014-06-20 11:00:47 +0000
547+++ http13client/transfer.go 2014-03-19 22:27:37 +0000549+++ http13client/transfer.go 2014-06-20 12:05:53 +0000
548@@ -11,6 +11,7 @@550@@ -11,6 +11,7 @@
549 "fmt"551 "fmt"
550 "io"552 "io"
551 "io/ioutil"553 "io/ioutil"
552+ "net/http"554+ "net/http"
553 "net/textproto"555 "net/textproto"
556 "sort"
554 "strconv"557 "strconv"
555 "strings"558@@ -37,7 +38,7 @@
556@@ -36,7 +37,7 @@
557 ContentLength int64 // -1 means unknown, 0 means exactly none559 ContentLength int64 // -1 means unknown, 0 means exactly none
558 Close bool560 Close bool
559 TransferEncoding []string561 TransferEncoding []string
@@ -562,16 +564,16 @@
562 }564 }
563 565
564 func newTransferWriter(r interface{}) (t *transferWriter, err error) {566 func newTransferWriter(r interface{}) (t *transferWriter, err error) {
565@@ -174,7 +175,7 @@567@@ -171,7 +172,7 @@
566 io.WriteString(w, "Trailer: ")568 if t.Trailer != nil {
567 needComma := false569 keys := make([]string, 0, len(t.Trailer))
568 for k := range t.Trailer {570 for k := range t.Trailer {
569- k = CanonicalHeaderKey(k)571- k = CanonicalHeaderKey(k)
570+ k = http.CanonicalHeaderKey(k)572+ k = http.CanonicalHeaderKey(k)
571 switch k {573 switch k {
572 case "Transfer-Encoding", "Trailer", "Content-Length":574 case "Transfer-Encoding", "Trailer", "Content-Length":
573 return &badStringError{"invalid Trailer key", k}575 return &badStringError{"invalid Trailer key", k}
574@@ -237,7 +238,7 @@576@@ -243,7 +244,7 @@
575 577
576 type transferReader struct {578 type transferReader struct {
577 // Input579 // Input
@@ -580,7 +582,7 @@
580 StatusCode int582 StatusCode int
581 RequestMethod string583 RequestMethod string
582 ProtoMajor int584 ProtoMajor int
583@@ -247,7 +248,7 @@585@@ -253,7 +254,7 @@
584 ContentLength int64586 ContentLength int64
585 TransferEncoding []string587 TransferEncoding []string
586 Close bool588 Close bool
@@ -589,7 +591,7 @@
589 }591 }
590 592
591 // bodyAllowedForStatus reports whether a given response status code593 // bodyAllowedForStatus reports whether a given response status code
592@@ -308,7 +309,7 @@594@@ -330,7 +331,7 @@
593 return err595 return err
594 }596 }
595 if isResponse && t.RequestMethod == "HEAD" {597 if isResponse && t.RequestMethod == "HEAD" {
@@ -598,7 +600,7 @@
598 return err600 return err
599 } else {601 } else {
600 t.ContentLength = n602 t.ContentLength = n
601@@ -386,7 +387,7 @@603@@ -408,7 +409,7 @@
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" }
603 605
604 // Sanitize transfer encoding606 // Sanitize transfer encoding
@@ -607,7 +609,7 @@
607 raw, present := header["Transfer-Encoding"]609 raw, present := header["Transfer-Encoding"]
608 if !present {610 if !present {
609 return nil, nil611 return nil, nil
610@@ -429,7 +430,7 @@612@@ -451,7 +452,7 @@
611 // Determine the expected body length, using RFC 2616 Section 4.4. This613 // Determine the expected body length, using RFC 2616 Section 4.4. This
612 // function is not a method, because ultimately it should be shared by614 // function is not a method, because ultimately it should be shared by
613 // ReadResponse and ReadRequest.615 // ReadResponse and ReadRequest.
@@ -616,7 +618,7 @@
616 618
617 // Logic based on response type or status619 // Logic based on response type or status
618 if noBodyExpected(requestMethod) {620 if noBodyExpected(requestMethod) {
619@@ -449,7 +450,7 @@621@@ -471,7 +472,7 @@
620 }622 }
621 623
622 // Logic based on Content-Length624 // Logic based on Content-Length
@@ -625,7 +627,7 @@
625 if cl != "" {627 if cl != "" {
626 n, err := parseContentLength(cl)628 n, err := parseContentLength(cl)
627 if err != nil {629 if err != nil {
628@@ -475,18 +476,18 @@630@@ -497,18 +498,18 @@
629 // Determine whether to hang up after sending a request and body, or631 // Determine whether to hang up after sending a request and body, or
630 // receiving a response and body632 // receiving a response and body
631 // 'header' is the request headers633 // 'header' is the request headers
@@ -647,7 +649,7 @@
647 header.Del("Connection")649 header.Del("Connection")
648 return true650 return true
649 }651 }
650@@ -495,17 +496,17 @@652@@ -517,17 +518,17 @@
651 }653 }
652 654
653 // Parse the trailer header655 // Parse the trailer header
@@ -669,22 +671,28 @@
669 switch key {671 switch key {
670 case "Transfer-Encoding", "Trailer", "Content-Length":672 case "Transfer-Encoding", "Trailer", "Content-Length":
671 return nil, &badStringError{"bad trailer key", key}673 return nil, &badStringError{"bad trailer key", key}
672@@ -642,9 +643,9 @@674@@ -664,14 +665,14 @@
673 }675 }
674 switch rr := b.hdr.(type) {676 switch rr := b.hdr.(type) {
675 case *Request:677 case *Request:
676- rr.Trailer = Header(hdr)678- mergeSetHeader(&rr.Trailer, Header(hdr))
677+ rr.Trailer = http.Header(hdr)679+ mergeSetHeader(&rr.Trailer, http.Header(hdr))
678 case *Response:680 case *Response:
679- rr.Trailer = Header(hdr)681- mergeSetHeader(&rr.Trailer, Header(hdr))
680+ rr.Trailer = http.Header(hdr)682+ mergeSetHeader(&rr.Trailer, http.Header(hdr))
681 }683 }
682 return nil684 return nil
683 }685 }
686
687-func mergeSetHeader(dst *Header, src Header) {
688+func mergeSetHeader(dst *http.Header, src http.Header) {
689 if *dst == nil {
690 *dst = src
691 return
684692
685=== modified file 'http13client/transport.go'693=== modified file 'http13client/transport.go'
686--- http13client/transport.go 2014-03-19 20:20:19 +0000694--- http13client/transport.go 2014-06-20 11:00:47 +0000
687+++ http13client/transport.go 2014-03-19 22:27:37 +0000695+++ http13client/transport.go 2014-06-20 12:05:53 +0000
688@@ -18,6 +18,7 @@696@@ -18,6 +18,7 @@
689 "io"697 "io"
690 "log"698 "log"
@@ -693,7 +701,7 @@
693 "net/url"701 "net/url"
694 "os"702 "os"
695 "strings"703 "strings"
696@@ -144,12 +145,12 @@704@@ -147,12 +148,12 @@
697 // optional extra headers to write.705 // optional extra headers to write.
698 type transportRequest struct {706 type transportRequest struct {
699 *Request // original request, not to be mutated707 *Request // original request, not to be mutated
@@ -709,7 +717,7 @@
709 }717 }
710 return tr.extra718 return tr.extra
711 }719 }
712@@ -512,7 +513,7 @@720@@ -519,7 +520,7 @@
713 case cm.targetScheme == "http":721 case cm.targetScheme == "http":
714 pconn.isProxy = true722 pconn.isProxy = true
715 if pa != "" {723 if pa != "" {
@@ -718,7 +726,7 @@
718 h.Set("Proxy-Authorization", pa)726 h.Set("Proxy-Authorization", pa)
719 }727 }
720 }728 }
721@@ -521,7 +522,7 @@729@@ -528,7 +529,7 @@
722 Method: "CONNECT",730 Method: "CONNECT",
723 URL: &url.URL{Opaque: cm.targetAddr},731 URL: &url.URL{Opaque: cm.targetAddr},
724 Host: cm.targetAddr,732 Host: cm.targetAddr,
@@ -727,7 +735,7 @@
727 }735 }
728 if pa != "" {736 if pa != "" {
729 connectReq.Header.Set("Proxy-Authorization", pa)737 connectReq.Header.Set("Proxy-Authorization", pa)
730@@ -735,7 +736,7 @@738@@ -748,7 +749,7 @@
731 // mutateHeaderFunc is an optional func to modify extra739 // mutateHeaderFunc is an optional func to modify extra
732 // headers on each outbound request before it's written. (the740 // headers on each outbound request before it's written. (the
733 // original Request given to RoundTrip is not modified)741 // original Request given to RoundTrip is not modified)
@@ -735,5 +743,5 @@
735+ mutateHeaderFunc func(http.Header)743+ mutateHeaderFunc func(http.Header)
736 }744 }
737 745
738 func (pc *persistConn) isBroken() bool {746 // isBroken reports whether this connection is in a known broken state.
739747
740748
=== modified file 'http13client/_patches/fix_status.patch'
--- http13client/_patches/fix_status.patch 2014-03-19 23:43:25 +0000
+++ http13client/_patches/fix_status.patch 2014-07-02 13:12:36 +0000
@@ -1,7 +1,7 @@
1=== modified file 'http13client/client.go'1=== modified file 'http13client/client.go'
2--- http13client/client.go 2014-03-19 23:13:58 +00002--- http13client/client.go 2014-06-20 12:46:25 +0000
3+++ http13client/client.go 2014-03-19 23:38:11 +00003+++ http13client/client.go 2014-06-20 12:46:45 +0000
4@@ -210,7 +210,7 @@4@@ -217,7 +217,7 @@
5 // automatically redirect.5 // automatically redirect.
6 func shouldRedirectGet(statusCode int) bool {6 func shouldRedirectGet(statusCode int) bool {
7 switch statusCode {7 switch statusCode {
@@ -10,7 +10,7 @@
10 return true10 return true
11 }11 }
12 return false12 return false
13@@ -220,7 +220,7 @@13@@ -227,7 +227,7 @@
14 // automatically redirect.14 // automatically redirect.
15 func shouldRedirectPost(statusCode int) bool {15 func shouldRedirectPost(statusCode int) bool {
16 switch statusCode {16 switch statusCode {
@@ -21,9 +21,9 @@
21 return false21 return false
2222
23=== modified file 'http13client/client_test.go'23=== modified file 'http13client/client_test.go'
24--- http13client/client_test.go 2014-03-19 23:13:58 +000024--- http13client/client_test.go 2014-06-20 12:46:25 +0000
25+++ http13client/client_test.go 2014-03-19 23:39:48 +000025+++ http13client/client_test.go 2014-06-20 12:46:45 +0000
26@@ -202,7 +202,7 @@26@@ -204,7 +204,7 @@
27 }27 }
28 }28 }
29 if n < 15 {29 if n < 15 {
@@ -32,7 +32,7 @@
32 return32 return
33 }33 }
34 fmt.Fprintf(w, "n=%d", n)34 fmt.Fprintf(w, "n=%d", n)
35@@ -324,7 +324,7 @@35@@ -326,7 +326,7 @@
36 }36 }
37 if r.URL.Path == "/" {37 if r.URL.Path == "/" {
38 http.SetCookie(w, expectedCookies[1])38 http.SetCookie(w, expectedCookies[1])
@@ -41,7 +41,7 @@
41 } else {41 } else {
42 http.SetCookie(w, expectedCookies[2])42 http.SetCookie(w, expectedCookies[2])
43 w.Write([]byte("hello"))43 w.Write([]byte("hello"))
44@@ -783,7 +783,7 @@44@@ -785,7 +785,7 @@
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) {
46 if r.URL.Path == "/" {46 if r.URL.Path == "/" {
47 sawRoot <- true47 sawRoot <- true
@@ -50,7 +50,7 @@
50 return50 return
51 }51 }
52 if r.URL.Path == "/slow" {52 if r.URL.Path == "/slow" {
53@@ -844,7 +844,7 @@53@@ -846,7 +846,7 @@
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) {
55 saw <- r.RemoteAddr55 saw <- r.RemoteAddr
56 if r.URL.Path == "/" {56 if r.URL.Path == "/" {
@@ -61,9 +61,9 @@
61 defer ts.Close()61 defer ts.Close()
6262
63=== modified file 'http13client/request_test.go'63=== modified file 'http13client/request_test.go'
64--- http13client/request_test.go 2014-03-19 23:13:58 +000064--- http13client/request_test.go 2014-06-20 12:46:25 +0000
65+++ http13client/request_test.go 2014-03-19 23:40:12 +000065+++ http13client/request_test.go 2014-06-20 12:46:45 +0000
66@@ -164,11 +164,11 @@66@@ -182,11 +182,11 @@
67 switch r.URL.Path {67 switch r.URL.Path {
68 case "/":68 case "/":
69 w.Header().Set("Location", "/foo/")69 w.Header().Set("Location", "/foo/")
@@ -79,9 +79,9 @@
79 defer ts.Close()79 defer ts.Close()
8080
81=== modified file 'http13client/response.go'81=== modified file 'http13client/response.go'
82--- http13client/response.go 2014-03-19 23:13:58 +000082--- http13client/response.go 2014-06-20 12:46:25 +0000
83+++ http13client/response.go 2014-03-19 23:38:56 +000083+++ http13client/response.go 2014-06-20 12:46:45 +0000
84@@ -204,9 +204,8 @@84@@ -205,9 +205,8 @@
85 // Status line85 // Status line
86 text := r.Status86 text := r.Status
87 if text == "" {87 if text == "" {
@@ -94,10 +94,23 @@
94 }94 }
95 }95 }
9696
97=== modified file 'http13client/responsewrite_test.go'
98--- http13client/responsewrite_test.go 2014-06-20 12:46:25 +0000
99+++ http13client/responsewrite_test.go 2014-06-20 12:47:05 +0000
100@@ -197,7 +197,7 @@
101 // there were two.
102 {
103 Response{
104- StatusCode: StatusOK,
105+ StatusCode: http.StatusOK,
106 ProtoMajor: 1,
107 ProtoMinor: 1,
108 Request: &Request{Method: "POST"},
109
97=== modified file 'http13client/transport_test.go'110=== modified file 'http13client/transport_test.go'
98--- http13client/transport_test.go 2014-03-19 23:13:58 +0000111--- http13client/transport_test.go 2014-06-20 12:46:25 +0000
99+++ http13client/transport_test.go 2014-03-19 23:40:39 +0000112+++ http13client/transport_test.go 2014-06-20 12:46:45 +0000
100@@ -968,7 +968,7 @@113@@ -1004,7 +1004,7 @@
101 defer afterTest(t)114 defer afterTest(t)
102 const deniedMsg = "sorry, denied."115 const deniedMsg = "sorry, denied."
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) {
@@ -106,7 +119,7 @@
106 }))119 }))
107 defer ts.Close()120 defer ts.Close()
108 tr := &Transport{}121 tr := &Transport{}
109@@ -992,7 +992,7 @@122@@ -1028,7 +1028,7 @@
110 func TestChunkedNoContent(t *testing.T) {123 func TestChunkedNoContent(t *testing.T) {
111 defer afterTest(t)124 defer afterTest(t)
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) {
113126
=== modified file 'http13client/_patches/fix_tests.patch'
--- http13client/_patches/fix_tests.patch 2014-03-19 23:13:58 +0000
+++ http13client/_patches/fix_tests.patch 2014-07-02 13:12:36 +0000
@@ -1,6 +1,6 @@
1=== modified file 'http13client/client_test.go'1=== modified file 'http13client/client_test.go'
2--- http13client/client_test.go 2014-03-19 21:38:56 +00002--- http13client/client_test.go 2014-06-20 11:00:47 +0000
3+++ http13client/client_test.go 2014-03-19 22:27:37 +00003+++ http13client/client_test.go 2014-06-20 12:05:53 +0000
4@@ -15,8 +15,8 @@4@@ -15,8 +15,8 @@
5 "fmt"5 "fmt"
6 "io"6 "io"
@@ -11,7 +11,7 @@
11 . "launchpad.net/ubuntu-push/http13client"11 . "launchpad.net/ubuntu-push/http13client"
12 "net/http/httptest"12 "net/http/httptest"
13 "net/url"13 "net/url"
14@@ -27,7 +27,7 @@14@@ -29,7 +29,7 @@
15 "time"15 "time"
16 )16 )
17 17
@@ -20,7 +20,7 @@
20 w.Header().Set("Last-Modified", "sometime")20 w.Header().Set("Last-Modified", "sometime")
21 fmt.Fprintf(w, "User-agent: go\nDisallow: /something/")21 fmt.Fprintf(w, "User-agent: go\nDisallow: /something/")
22 })22 })
23@@ -193,7 +193,7 @@23@@ -195,7 +195,7 @@
24 func TestClientRedirects(t *testing.T) {24 func TestClientRedirects(t *testing.T) {
25 defer afterTest(t)25 defer afterTest(t)
26 var ts *httptest.Server26 var ts *httptest.Server
@@ -29,7 +29,7 @@
29 n, _ := strconv.Atoi(r.FormValue("n"))29 n, _ := strconv.Atoi(r.FormValue("n"))
30 // Test Referer header. (7 is arbitrary position to test at)30 // Test Referer header. (7 is arbitrary position to test at)
31 if n == 7 {31 if n == 7 {
32@@ -202,7 +202,7 @@32@@ -204,7 +204,7 @@
33 }33 }
34 }34 }
35 if n < 15 {35 if n < 15 {
@@ -38,7 +38,7 @@
38 return38 return
39 }39 }
40 fmt.Fprintf(w, "n=%d", n)40 fmt.Fprintf(w, "n=%d", n)
41@@ -271,7 +271,7 @@41@@ -273,7 +273,7 @@
42 bytes.Buffer42 bytes.Buffer
43 }43 }
44 var ts *httptest.Server44 var ts *httptest.Server
@@ -47,7 +47,7 @@
47 log.Lock()47 log.Lock()
48 fmt.Fprintf(&log.Buffer, "%s %s ", r.Method, r.RequestURI)48 fmt.Fprintf(&log.Buffer, "%s %s ", r.Method, r.RequestURI)
49 log.Unlock()49 log.Unlock()
50@@ -312,21 +312,21 @@50@@ -314,21 +314,21 @@
51 }51 }
52 }52 }
53 53
@@ -75,7 +75,7 @@
75 w.Write([]byte("hello"))75 w.Write([]byte("hello"))
76 }76 }
77 })77 })
78@@ -334,7 +334,7 @@78@@ -336,7 +336,7 @@
79 func TestClientSendsCookieFromJar(t *testing.T) {79 func TestClientSendsCookieFromJar(t *testing.T) {
80 tr := &recordingTransport{}80 tr := &recordingTransport{}
81 client := &Client{Transport: tr}81 client := &Client{Transport: tr}
@@ -84,7 +84,7 @@
84 us := "http://dummy.faketld/"84 us := "http://dummy.faketld/"
85 u, _ := url.Parse(us)85 u, _ := url.Parse(us)
86 client.Jar.SetCookies(u, expectedCookies)86 client.Jar.SetCookies(u, expectedCookies)
87@@ -364,19 +364,19 @@87@@ -366,19 +366,19 @@
88 // scope of all cookies.88 // scope of all cookies.
89 type TestJar struct {89 type TestJar struct {
90 m sync.Mutex90 m sync.Mutex
@@ -108,7 +108,7 @@
108 j.m.Lock()108 j.m.Lock()
109 defer j.m.Unlock()109 defer j.m.Unlock()
110 return j.perURL[u.Host]110 return j.perURL[u.Host]
111@@ -391,7 +391,7 @@111@@ -393,7 +393,7 @@
112 Jar: new(TestJar),112 Jar: new(TestJar),
113 }113 }
114 u, _ := url.Parse(ts.URL)114 u, _ := url.Parse(ts.URL)
@@ -117,7 +117,7 @@
117 resp, err := c.Get(ts.URL)117 resp, err := c.Get(ts.URL)
118 if err != nil {118 if err != nil {
119 t.Fatalf("Get: %v", err)119 t.Fatalf("Get: %v", err)
120@@ -400,7 +400,7 @@120@@ -402,7 +402,7 @@
121 matchReturnedCookies(t, expectedCookies, resp.Cookies())121 matchReturnedCookies(t, expectedCookies, resp.Cookies())
122 }122 }
123 123
@@ -126,7 +126,7 @@
126 if len(given) != len(expected) {126 if len(given) != len(expected) {
127 t.Logf("Received cookies: %v", given)127 t.Logf("Received cookies: %v", given)
128 t.Errorf("Expected %d cookies, got %d", len(expected), len(given))128 t.Errorf("Expected %d cookies, got %d", len(expected), len(given))
129@@ -421,14 +421,14 @@129@@ -423,14 +423,14 @@
130 130
131 func TestJarCalls(t *testing.T) {131 func TestJarCalls(t *testing.T) {
132 defer afterTest(t)132 defer afterTest(t)
@@ -144,7 +144,7 @@
144 }144 }
145 }))145 }))
146 defer ts.Close()146 defer ts.Close()
147@@ -468,11 +468,11 @@147@@ -470,11 +470,11 @@
148 log bytes.Buffer148 log bytes.Buffer
149 }149 }
150 150
@@ -158,7 +158,7 @@
158 j.logf("Cookies(%q)\n", u)158 j.logf("Cookies(%q)\n", u)
159 return nil159 return nil
160 }160 }
161@@ -486,11 +486,11 @@161@@ -488,11 +488,11 @@
162 func TestStreamingGet(t *testing.T) {162 func TestStreamingGet(t *testing.T) {
163 defer afterTest(t)163 defer afterTest(t)
164 say := make(chan string)164 say := make(chan string)
@@ -173,7 +173,7 @@
173 }173 }
174 }))174 }))
175 defer ts.Close()175 defer ts.Close()
176@@ -536,7 +536,7 @@176@@ -538,7 +538,7 @@
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.
178 func TestClientWrites(t *testing.T) {178 func TestClientWrites(t *testing.T) {
179 defer afterTest(t)179 defer afterTest(t)
@@ -182,7 +182,7 @@
182 }))182 }))
183 defer ts.Close()183 defer ts.Close()
184 184
185@@ -568,46 +568,6 @@185@@ -570,46 +570,6 @@
186 }186 }
187 }187 }
188 188
@@ -229,7 +229,7 @@
229 func TestClientErrorWithRequestURI(t *testing.T) {229 func TestClientErrorWithRequestURI(t *testing.T) {
230 defer afterTest(t)230 defer afterTest(t)
231 req, _ := NewRequest("GET", "http://localhost:1234/", nil)231 req, _ := NewRequest("GET", "http://localhost:1234/", nil)
232@@ -639,7 +599,7 @@232@@ -641,7 +601,7 @@
233 233
234 func TestClientWithCorrectTLSServerName(t *testing.T) {234 func TestClientWithCorrectTLSServerName(t *testing.T) {
235 defer afterTest(t)235 defer afterTest(t)
@@ -238,7 +238,7 @@
238 if r.TLS.ServerName != "127.0.0.1" {238 if r.TLS.ServerName != "127.0.0.1" {
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)
240 }240 }
241@@ -652,33 +612,6 @@241@@ -654,33 +614,6 @@
242 }242 }
243 }243 }
244 244
@@ -272,7 +272,7 @@
272 // Test for golang.org/issue/5829; the Transport should respect TLSClientConfig.ServerName272 // Test for golang.org/issue/5829; the Transport should respect TLSClientConfig.ServerName
273 // when not empty.273 // when not empty.
274 //274 //
275@@ -690,7 +623,7 @@275@@ -692,7 +625,7 @@
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.
277 func TestTransportUsesTLSConfigServerName(t *testing.T) {277 func TestTransportUsesTLSConfigServerName(t *testing.T) {
278 defer afterTest(t)278 defer afterTest(t)
@@ -281,7 +281,7 @@
281 w.Write([]byte("Hello"))281 w.Write([]byte("Hello"))
282 }))282 }))
283 defer ts.Close()283 defer ts.Close()
284@@ -711,7 +644,7 @@284@@ -713,7 +646,7 @@
285 285
286 func TestResponseSetsTLSConnectionState(t *testing.T) {286 func TestResponseSetsTLSConnectionState(t *testing.T) {
287 defer afterTest(t)287 defer afterTest(t)
@@ -290,7 +290,7 @@
290 w.Write([]byte("Hello"))290 w.Write([]byte("Hello"))
291 }))291 }))
292 defer ts.Close()292 defer ts.Close()
293@@ -739,7 +672,7 @@293@@ -741,7 +674,7 @@
294 // Verify Response.ContentLength is populated. http://golang.org/issue/4126294 // Verify Response.ContentLength is populated. http://golang.org/issue/4126
295 func TestClientHeadContentLength(t *testing.T) {295 func TestClientHeadContentLength(t *testing.T) {
296 defer afterTest(t)296 defer afterTest(t)
@@ -299,7 +299,7 @@
299 if v := r.FormValue("cl"); v != "" {299 if v := r.FormValue("cl"); v != "" {
300 w.Header().Set("Content-Length", v)300 w.Header().Set("Content-Length", v)
301 }301 }
302@@ -775,7 +708,7 @@302@@ -777,7 +710,7 @@
303 func TestEmptyPasswordAuth(t *testing.T) {303 func TestEmptyPasswordAuth(t *testing.T) {
304 defer afterTest(t)304 defer afterTest(t)
305 gopher := "gopher"305 gopher := "gopher"
@@ -308,7 +308,7 @@
308 auth := r.Header.Get("Authorization")308 auth := r.Header.Get("Authorization")
309 if strings.HasPrefix(auth, "Basic ") {309 if strings.HasPrefix(auth, "Basic ") {
310 encoded := auth[6:]310 encoded := auth[6:]
311@@ -847,15 +780,15 @@311@@ -849,15 +782,15 @@
312 defer afterTest(t)312 defer afterTest(t)
313 sawRoot := make(chan bool, 1)313 sawRoot := make(chan bool, 1)
314 sawSlow := make(chan bool, 1)314 sawSlow := make(chan bool, 1)
@@ -327,7 +327,7 @@
327 sawSlow <- true327 sawSlow <- true
328 time.Sleep(2 * time.Second)328 time.Sleep(2 * time.Second)
329 return329 return
330@@ -908,10 +841,10 @@330@@ -910,10 +843,10 @@
331 func TestClientRedirectEatsBody(t *testing.T) {331 func TestClientRedirectEatsBody(t *testing.T) {
332 defer afterTest(t)332 defer afterTest(t)
333 saw := make(chan string, 2)333 saw := make(chan string, 2)
@@ -340,233 +340,50 @@
340 }340 }
341 }))341 }))
342 defer ts.Close()342 defer ts.Close()
343343@@ -957,7 +890,7 @@
344=== modified file 'http13client/cookie_test.go'344
345--- http13client/cookie_test.go 2014-03-19 20:20:19 +0000345 func TestClientTrailers(t *testing.T) {
346+++ http13client/cookie_test.go 2014-03-19 22:27:37 +0000346 defer afterTest(t)
347@@ -9,6 +9,7 @@347- ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
348 "encoding/json"348+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
349 "fmt"349 w.Header().Set("Connection", "close")
350 "log"350 w.Header().Set("Trailer", "Server-Trailer-A, Server-Trailer-B")
351+ "net/http"351 w.Header().Add("Trailer", "Server-Trailer-C")
352 "os"352@@ -992,9 +925,9 @@
353 "reflect"353 // trailers to be sent, if and only if they were
354 "strings"354 // previously declared with w.Header().Set("Trailer",
355@@ -17,39 +18,39 @@355 // ..keys..)
356 )356- w.(Flusher).Flush()
357 357- conn, buf, _ := w.(Hijacker).Hijack()
358 var writeSetCookiesTests = []struct {358- t := Header{}
359- Cookie *Cookie359+ w.(http.Flusher).Flush()
360+ Cookie *http.Cookie360+ conn, buf, _ := w.(http.Hijacker).Hijack()
361 Raw string361+ t := http.Header{}
362 }{362 t.Set("Server-Trailer-A", "valuea")
363 {363 t.Set("Server-Trailer-C", "valuec") // skipping B
364- &Cookie{Name: "cookie-1", Value: "v$1"},364 buf.WriteString("0\r\n") // eof
365+ &http.Cookie{Name: "cookie-1", Value: "v$1"},365@@ -1015,7 +948,7 @@
366 "cookie-1=v$1",366 req.Trailer["Client-Trailer-B"] = []string{"valueb"}
367 },367 }),
368 {368 ))
369- &Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600},369- req.Trailer = Header{
370+ &http.Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600},370+ req.Trailer = http.Header{
371 "cookie-2=two; Max-Age=3600",371 "Client-Trailer-A": nil, // to be set later
372 },372 "Client-Trailer-B": nil, // to be set later
373 {373 }
374- &Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"},374@@ -1027,7 +960,7 @@
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 {
376 "cookie-3=three; Domain=example.com",376 t.Error(err)
377 },377 }
378 {378- want := Header{
379- &Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"},379+ want := http.Header{
380+ &http.Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"},380 "Server-Trailer-A": []string{"valuea"},
381 "cookie-4=four; Path=/restricted/",381 "Server-Trailer-B": nil,
382 },382 "Server-Trailer-C": []string{"valuec"},
383 {
384- &Cookie{Name: "cookie-5", Value: "five", Domain: "wrong;bad.abc"},
385+ &http.Cookie{Name: "cookie-5", Value: "five", Domain: "wrong;bad.abc"},
386 "cookie-5=five",
387 },
388 {
389- &Cookie{Name: "cookie-6", Value: "six", Domain: "bad-.abc"},
390+ &http.Cookie{Name: "cookie-6", Value: "six", Domain: "bad-.abc"},
391 "cookie-6=six",
392 },
393 {
394- &Cookie{Name: "cookie-7", Value: "seven", Domain: "127.0.0.1"},
395+ &http.Cookie{Name: "cookie-7", Value: "seven", Domain: "127.0.0.1"},
396 "cookie-7=seven; Domain=127.0.0.1",
397 },
398 {
399- &Cookie{Name: "cookie-8", Value: "eight", Domain: "::1"},
400+ &http.Cookie{Name: "cookie-8", Value: "eight", Domain: "::1"},
401 "cookie-8=eight",
402 },
403 }
404@@ -71,10 +72,10 @@
405 }
406 }
407
408-type headerOnlyResponseWriter Header
409+type headerOnlyResponseWriter http.Header
410
411-func (ho headerOnlyResponseWriter) Header() Header {
412- return Header(ho)
413+func (ho headerOnlyResponseWriter) Header() http.Header {
414+ return http.Header(ho)
415 }
416
417 func (ho headerOnlyResponseWriter) Write([]byte) (int, error) {
418@@ -86,9 +87,9 @@
419 }
420
421 func TestSetCookie(t *testing.T) {
422- m := make(Header)
423- SetCookie(headerOnlyResponseWriter(m), &Cookie{Name: "cookie-1", Value: "one", Path: "/restricted/"})
424- SetCookie(headerOnlyResponseWriter(m), &Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600})
425+ m := make(http.Header)
426+ http.SetCookie(headerOnlyResponseWriter(m), &http.Cookie{Name: "cookie-1", Value: "one", Path: "/restricted/"})
427+ http.SetCookie(headerOnlyResponseWriter(m), &http.Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600})
428 if l := len(m["Set-Cookie"]); l != 2 {
429 t.Fatalf("expected %d cookies, got %d", 2, l)
430 }
431@@ -101,19 +102,19 @@
432 }
433
434 var addCookieTests = []struct {
435- Cookies []*Cookie
436+ Cookies []*http.Cookie
437 Raw string
438 }{
439 {
440- []*Cookie{},
441+ []*http.Cookie{},
442 "",
443 },
444 {
445- []*Cookie{{Name: "cookie-1", Value: "v$1"}},
446+ []*http.Cookie{{Name: "cookie-1", Value: "v$1"}},
447 "cookie-1=v$1",
448 },
449 {
450- []*Cookie{
451+ []*http.Cookie{
452 {Name: "cookie-1", Value: "v$1"},
453 {Name: "cookie-2", Value: "v$2"},
454 {Name: "cookie-3", Value: "v$3"},
455@@ -136,16 +137,16 @@
456 }
457
458 var readSetCookiesTests = []struct {
459- Header Header
460- Cookies []*Cookie
461+ Header http.Header
462+ Cookies []*http.Cookie
463 }{
464 {
465- Header{"Set-Cookie": {"Cookie-1=v$1"}},
466- []*Cookie{{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"}},
467+ http.Header{"Set-Cookie": {"Cookie-1=v$1"}},
468+ []*http.Cookie{{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"}},
469 },
470 {
471- Header{"Set-Cookie": {"NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly"}},
472- []*Cookie{{
473+ http.Header{"Set-Cookie": {"NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly"}},
474+ []*http.Cookie{{
475 Name: "NID",
476 Value: "99=YsDT5i3E-CXax-",
477 Path: "/",
478@@ -157,8 +158,8 @@
479 }},
480 },
481 {
482- Header{"Set-Cookie": {".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}},
483- []*Cookie{{
484+ http.Header{"Set-Cookie": {".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}},
485+ []*http.Cookie{{
486 Name: ".ASPXAUTH",
487 Value: "7E3AA",
488 Path: "/",
489@@ -169,8 +170,8 @@
490 }},
491 },
492 {
493- Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly"}},
494- []*Cookie{{
495+ http.Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly"}},
496+ []*http.Cookie{{
497 Name: "ASP.NET_SessionId",
498 Value: "foo",
499 Path: "/",
500@@ -207,37 +208,37 @@
501 }
502
503 var readCookiesTests = []struct {
504- Header Header
505+ Header http.Header
506 Filter string
507- Cookies []*Cookie
508+ Cookies []*http.Cookie
509 }{
510 {
511- Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
512- "",
513- []*Cookie{
514- {Name: "Cookie-1", Value: "v$1"},
515- {Name: "c2", Value: "v2"},
516- },
517- },
518- {
519- Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
520- "c2",
521- []*Cookie{
522- {Name: "c2", Value: "v2"},
523- },
524- },
525- {
526- Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
527- "",
528- []*Cookie{
529- {Name: "Cookie-1", Value: "v$1"},
530- {Name: "c2", Value: "v2"},
531- },
532- },
533- {
534- Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
535- "c2",
536- []*Cookie{
537+ http.Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
538+ "",
539+ []*http.Cookie{
540+ {Name: "Cookie-1", Value: "v$1"},
541+ {Name: "c2", Value: "v2"},
542+ },
543+ },
544+ {
545+ http.Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
546+ "c2",
547+ []*http.Cookie{
548+ {Name: "c2", Value: "v2"},
549+ },
550+ },
551+ {
552+ http.Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
553+ "",
554+ []*http.Cookie{
555+ {Name: "Cookie-1", Value: "v$1"},
556+ {Name: "c2", Value: "v2"},
557+ },
558+ },
559+ {
560+ http.Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
561+ "c2",
562+ []*http.Cookie{
563 {Name: "c2", Value: "v2"},
564 },
565 },
566383
567=== modified file 'http13client/export_test.go'384=== modified file 'http13client/export_test.go'
568--- http13client/export_test.go 2014-03-19 20:20:19 +0000385--- http13client/export_test.go 2014-06-20 11:00:47 +0000
569+++ http13client/export_test.go 2014-03-19 22:27:37 +0000386+++ http13client/export_test.go 2014-06-20 12:00:22 +0000
570@@ -9,15 +9,12 @@387@@ -9,15 +9,12 @@
571 388
572 import (389 import (
@@ -599,8 +416,8 @@
599 noProxyEnv.reset()416 noProxyEnv.reset()
600417
601=== modified file 'http13client/header_test.go'418=== modified file 'http13client/header_test.go'
602--- http13client/header_test.go 2014-03-19 20:20:19 +0000419--- http13client/header_test.go 2014-06-20 11:00:47 +0000
603+++ http13client/header_test.go 2014-03-19 22:27:37 +0000420+++ http13client/header_test.go 2014-06-20 12:00:22 +0000
604@@ -6,19 +6,20 @@421@@ -6,19 +6,20 @@
605 422
606 import (423 import (
@@ -731,8 +548,8 @@
731 }548 }
732549
733=== modified file 'http13client/npn_test.go'550=== modified file 'http13client/npn_test.go'
734--- http13client/npn_test.go 2014-03-19 21:38:56 +0000551--- http13client/npn_test.go 2014-06-20 11:00:47 +0000
735+++ http13client/npn_test.go 2014-03-19 22:27:37 +0000552+++ http13client/npn_test.go 2014-06-20 12:05:53 +0000
736@@ -11,13 +11,14 @@553@@ -11,13 +11,14 @@
737 "io"554 "io"
738 "io/ioutil"555 "io/ioutil"
@@ -792,8 +609,8 @@
792 func (w http09Writer) WriteHeader(int) {} // no headers609 func (w http09Writer) WriteHeader(int) {} // no headers
793610
794=== modified file 'http13client/readrequest_test.go'611=== modified file 'http13client/readrequest_test.go'
795--- http13client/readrequest_test.go 2014-03-19 20:20:19 +0000612--- http13client/readrequest_test.go 2014-06-20 11:00:47 +0000
796+++ http13client/readrequest_test.go 2014-03-19 22:27:37 +0000613+++ http13client/readrequest_test.go 2014-06-20 12:00:22 +0000
797@@ -9,6 +9,7 @@614@@ -9,6 +9,7 @@
798 "bytes"615 "bytes"
799 "fmt"616 "fmt"
@@ -909,8 +726,8 @@
909 Close: false,726 Close: false,
910727
911=== modified file 'http13client/request_test.go'728=== modified file 'http13client/request_test.go'
912--- http13client/request_test.go 2014-03-19 21:38:56 +0000729--- http13client/request_test.go 2014-06-20 11:00:47 +0000
913+++ http13client/request_test.go 2014-03-19 22:27:37 +0000730+++ http13client/request_test.go 2014-06-20 12:05:53 +0000
914@@ -12,6 +12,7 @@731@@ -12,6 +12,7 @@
915 "io/ioutil"732 "io/ioutil"
916 "mime/multipart"733 "mime/multipart"
@@ -945,8 +762,26 @@
945+ req.Header = http.Header{"Content-Type": {"text/plain"}}762+ req.Header = http.Header{"Content-Type": {"text/plain"}}
946 multipart, err = req.MultipartReader()763 multipart, err = req.MultipartReader()
947 if multipart != nil {764 if multipart != nil {
948 t.Errorf("unexpected multipart for text/plain")765 t.Error("unexpected multipart for text/plain")
949@@ -159,7 +160,7 @@766@@ -161,7 +162,7 @@
767 func TestParseMultipartForm(t *testing.T) {
768 req := &Request{
769 Method: "POST",
770- Header: Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}},
771+ Header: http.Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}},
772 Body: ioutil.NopCloser(new(bytes.Buffer)),
773 }
774 err := req.ParseMultipartForm(25)
775@@ -169,7 +170,7 @@
776 t.Error("expected multipart EOF, got nil")
777 }
778
779- req.Header = Header{"Content-Type": {"text/plain"}}
780+ req.Header = http.Header{"Content-Type": {"text/plain"}}
781 err = req.ParseMultipartForm(25)
782 if err != ErrNotMultipart {
783 t.Error("expected ErrNotMultipart for text/plain")
784@@ -177,7 +178,7 @@
950 }785 }
951 786
952 func TestRedirect(t *testing.T) {787 func TestRedirect(t *testing.T) {
@@ -957,8 +792,8 @@
957 w.Header().Set("Location", "/foo/")792 w.Header().Set("Location", "/foo/")
958793
959=== modified file 'http13client/requestwrite_test.go'794=== modified file 'http13client/requestwrite_test.go'
960--- http13client/requestwrite_test.go 2014-03-19 20:20:19 +0000795--- http13client/requestwrite_test.go 2014-06-20 11:00:47 +0000
961+++ http13client/requestwrite_test.go 2014-03-19 22:27:37 +0000796+++ http13client/requestwrite_test.go 2014-06-20 12:00:22 +0000
962@@ -10,6 +10,7 @@797@@ -10,6 +10,7 @@
963 "fmt"798 "fmt"
964 "io"799 "io"
@@ -1068,8 +903,8 @@
1068 var braw bytes.Buffer903 var braw bytes.Buffer
1069904
1070=== modified file 'http13client/response_test.go'905=== modified file 'http13client/response_test.go'
1071--- http13client/response_test.go 2014-03-19 20:20:19 +0000906--- http13client/response_test.go 2014-06-20 11:00:47 +0000
1072+++ http13client/response_test.go 2014-03-19 22:27:37 +0000907+++ http13client/response_test.go 2014-06-20 12:05:53 +0000
1073@@ -12,6 +12,7 @@908@@ -12,6 +12,7 @@
1074 "fmt"909 "fmt"
1075 "io"910 "io"
@@ -1078,7 +913,7 @@
1078 "net/url"913 "net/url"
1079 "reflect"914 "reflect"
1080 "regexp"915 "regexp"
1081@@ -44,7 +45,7 @@916@@ -48,7 +49,7 @@
1082 ProtoMajor: 1,917 ProtoMajor: 1,
1083 ProtoMinor: 0,918 ProtoMinor: 0,
1084 Request: dummyReq("GET"),919 Request: dummyReq("GET"),
@@ -1087,7 +922,7 @@
1087 "Connection": {"close"}, // TODO(rsc): Delete?922 "Connection": {"close"}, // TODO(rsc): Delete?
1088 },923 },
1089 Close: true,924 Close: true,
1090@@ -67,7 +68,7 @@925@@ -71,7 +72,7 @@
1091 Proto: "HTTP/1.1",926 Proto: "HTTP/1.1",
1092 ProtoMajor: 1,927 ProtoMajor: 1,
1093 ProtoMinor: 1,928 ProtoMinor: 1,
@@ -1096,7 +931,7 @@
1096 Request: dummyReq("GET"),931 Request: dummyReq("GET"),
1097 Close: true,932 Close: true,
1098 ContentLength: -1,933 ContentLength: -1,
1099@@ -88,7 +89,7 @@934@@ -92,7 +93,7 @@
1100 Proto: "HTTP/1.1",935 Proto: "HTTP/1.1",
1101 ProtoMajor: 1,936 ProtoMajor: 1,
1102 ProtoMinor: 1,937 ProtoMinor: 1,
@@ -1105,7 +940,7 @@
1105 Request: dummyReq("GET"),940 Request: dummyReq("GET"),
1106 Close: false,941 Close: false,
1107 ContentLength: 0,942 ContentLength: 0,
1108@@ -112,7 +113,7 @@943@@ -116,7 +117,7 @@
1109 ProtoMajor: 1,944 ProtoMajor: 1,
1110 ProtoMinor: 0,945 ProtoMinor: 0,
1111 Request: dummyReq("GET"),946 Request: dummyReq("GET"),
@@ -1114,61 +949,61 @@
1114 "Connection": {"close"},949 "Connection": {"close"},
1115 "Content-Length": {"10"},950 "Content-Length": {"10"},
1116 },951 },
1117@@ -142,7 +143,7 @@952@@ -146,7 +147,7 @@
1118 ProtoMajor: 1,953 ProtoMajor: 1,
1119 ProtoMinor: 1,954 ProtoMinor: 1,
1120 Request: dummyReq("GET"),955 Request: dummyReq("GET"),
1121- Header: Header{},956- Header: Header{},
1122+ Header: http.Header{},957+ Header: http.Header{},
1123 Close: false,958 Close: false,
1124 ContentLength: -1,959 ContentLength: -1,
1125 TransferEncoding: []string{"chunked"},960 TransferEncoding: []string{"chunked"},
1126@@ -169,7 +170,7 @@961@@ -173,7 +174,7 @@
1127 ProtoMajor: 1,962 ProtoMajor: 1,
1128 ProtoMinor: 1,963 ProtoMinor: 1,
1129 Request: dummyReq("GET"),964 Request: dummyReq("GET"),
1130- Header: Header{},965- Header: Header{},
1131+ Header: http.Header{},966+ Header: http.Header{},
1132 Close: false,967 Close: false,
1133 ContentLength: -1,968 ContentLength: -1,
1134 TransferEncoding: []string{"chunked"},969 TransferEncoding: []string{"chunked"},
1135@@ -191,7 +192,7 @@970@@ -195,7 +196,7 @@
1136 ProtoMajor: 1,971 ProtoMajor: 1,
1137 ProtoMinor: 1,972 ProtoMinor: 1,
1138 Request: dummyReq("HEAD"),973 Request: dummyReq("HEAD"),
1139- Header: Header{},974- Header: Header{},
1140+ Header: http.Header{},975+ Header: http.Header{},
1141 TransferEncoding: []string{"chunked"},976 TransferEncoding: []string{"chunked"},
1142 Close: false,977 Close: false,
1143 ContentLength: -1,978 ContentLength: -1,
1144@@ -213,7 +214,7 @@979@@ -217,7 +218,7 @@
1145 ProtoMajor: 1,980 ProtoMajor: 1,
1146 ProtoMinor: 0,981 ProtoMinor: 0,
1147 Request: dummyReq("HEAD"),982 Request: dummyReq("HEAD"),
1148- Header: Header{"Content-Length": {"256"}},983- Header: Header{"Content-Length": {"256"}},
1149+ Header: http.Header{"Content-Length": {"256"}},984+ Header: http.Header{"Content-Length": {"256"}},
1150 TransferEncoding: nil,985 TransferEncoding: nil,
1151 Close: true,986 Close: true,
1152 ContentLength: 256,987 ContentLength: 256,
1153@@ -235,7 +236,7 @@988@@ -239,7 +240,7 @@
1154 ProtoMajor: 1,989 ProtoMajor: 1,
1155 ProtoMinor: 1,990 ProtoMinor: 1,
1156 Request: dummyReq("HEAD"),991 Request: dummyReq("HEAD"),
1157- Header: Header{"Content-Length": {"256"}},992- Header: Header{"Content-Length": {"256"}},
1158+ Header: http.Header{"Content-Length": {"256"}},993+ Header: http.Header{"Content-Length": {"256"}},
1159 TransferEncoding: nil,994 TransferEncoding: nil,
1160 Close: false,995 Close: false,
1161 ContentLength: 256,996 ContentLength: 256,
1162@@ -256,7 +257,7 @@997@@ -260,7 +261,7 @@
1163 ProtoMajor: 1,998 ProtoMajor: 1,
1164 ProtoMinor: 0,999 ProtoMinor: 0,
1165 Request: dummyReq("HEAD"),1000 Request: dummyReq("HEAD"),
1166- Header: Header{},1001- Header: Header{},
1167+ Header: http.Header{},1002+ Header: http.Header{},
1168 TransferEncoding: nil,1003 TransferEncoding: nil,
1169 Close: true,1004 Close: true,
1170 ContentLength: -1,1005 ContentLength: -1,
1171@@ -278,7 +279,7 @@1006@@ -282,7 +283,7 @@
1172 ProtoMajor: 1,1007 ProtoMajor: 1,
1173 ProtoMinor: 1,1008 ProtoMinor: 1,
1174 Request: dummyReq("GET"),1009 Request: dummyReq("GET"),
@@ -1177,25 +1012,25 @@
1177 "Content-Length": {"0"},1012 "Content-Length": {"0"},
1178 },1013 },
1179 Close: false,1014 Close: false,
1180@@ -299,7 +300,7 @@1015@@ -303,7 +304,7 @@
1181 ProtoMajor: 1,1016 ProtoMajor: 1,
1182 ProtoMinor: 0,1017 ProtoMinor: 0,
1183 Request: dummyReq("GET"),1018 Request: dummyReq("GET"),
1184- Header: Header{},1019- Header: Header{},
1185+ Header: http.Header{},1020+ Header: http.Header{},
1186 Close: true,1021 Close: true,
1187 ContentLength: -1,1022 ContentLength: -1,
1188 },1023 },
1189@@ -318,7 +319,7 @@1024@@ -322,7 +323,7 @@
1190 ProtoMajor: 1,1025 ProtoMajor: 1,
1191 ProtoMinor: 0,1026 ProtoMinor: 0,
1192 Request: dummyReq("GET"),1027 Request: dummyReq("GET"),
1193- Header: Header{},1028- Header: Header{},
1194+ Header: http.Header{},1029+ Header: http.Header{},
1195 Close: true,1030 Close: true,
1196 ContentLength: -1,1031 ContentLength: -1,
1197 },1032 },
1198@@ -340,7 +341,7 @@1033@@ -344,7 +345,7 @@
1199 ProtoMajor: 1,1034 ProtoMajor: 1,
1200 ProtoMinor: 1,1035 ProtoMinor: 1,
1201 Request: dummyReq("GET"),1036 Request: dummyReq("GET"),
@@ -1204,7 +1039,7 @@
1204 "Content-Type": []string{"multipart/byteranges; boundary=18a75608c8f47cef"},1039 "Content-Type": []string{"multipart/byteranges; boundary=18a75608c8f47cef"},
1205 },1040 },
1206 Close: true,1041 Close: true,
1207@@ -363,7 +364,7 @@1042@@ -367,7 +368,7 @@
1208 Proto: "HTTP/1.0",1043 Proto: "HTTP/1.0",
1209 ProtoMajor: 1,1044 ProtoMajor: 1,
1210 ProtoMinor: 0,1045 ProtoMinor: 0,
@@ -1213,7 +1048,7 @@
1213 "Connection": {"close"}, // TODO(rsc): Delete?1048 "Connection": {"close"}, // TODO(rsc): Delete?
1214 },1049 },
1215 Close: true,1050 Close: true,
1216@@ -545,7 +546,7 @@1051@@ -549,7 +550,7 @@
1217 func TestLocationResponse(t *testing.T) {1052 func TestLocationResponse(t *testing.T) {
1218 for i, tt := range responseLocationTests {1053 for i, tt := range responseLocationTests {
1219 res := new(Response)1054 res := new(Response)
@@ -1222,7 +1057,7 @@
1222 res.Header.Set("Location", tt.location)1057 res.Header.Set("Location", tt.location)
1223 if tt.requrl != "" {1058 if tt.requrl != "" {
1224 res.Request = &Request{}1059 res.Request = &Request{}
1225@@ -626,16 +627,3 @@1060@@ -630,16 +631,3 @@
1226 t.Errorf("ReadResponse = %v; want io.ErrUnexpectedEOF", err)1061 t.Errorf("ReadResponse = %v; want io.ErrUnexpectedEOF", err)
1227 }1062 }
1228 }1063 }
@@ -1241,8 +1076,8 @@
1241-}1076-}
12421077
1243=== modified file 'http13client/responsewrite_test.go'1078=== modified file 'http13client/responsewrite_test.go'
1244--- http13client/responsewrite_test.go 2014-03-19 20:20:19 +00001079--- http13client/responsewrite_test.go 2014-06-20 11:00:47 +0000
1245+++ http13client/responsewrite_test.go 2014-03-19 22:27:37 +00001080+++ http13client/responsewrite_test.go 2014-06-20 12:05:53 +0000
1246@@ -7,6 +7,7 @@1081@@ -7,6 +7,7 @@
1247 import (1082 import (
1248 "bytes"1083 "bytes"
@@ -1257,7 +1092,7 @@
1257 Request: dummyReq("GET"),1092 Request: dummyReq("GET"),
1258- Header: Header{},1093- Header: Header{},
1259+ Header: http.Header{},1094+ Header: http.Header{},
1260 Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")),1095 Body: ioutil.NopCloser(strings.NewReader("abcdef")),
1261 ContentLength: 6,1096 ContentLength: 6,
1262 },1097 },
1263@@ -41,7 +42,7 @@1098@@ -41,7 +42,7 @@
@@ -1270,6 +1105,60 @@
1270 ContentLength: -1,1105 ContentLength: -1,
1271 },1106 },
1272@@ -56,7 +57,7 @@1107@@ -56,7 +57,7 @@
1108 ProtoMajor: 1,
1109 ProtoMinor: 1,
1110 Request: dummyReq("GET"),
1111- Header: Header{},
1112+ Header: http.Header{},
1113 Body: ioutil.NopCloser(strings.NewReader("abcdef")),
1114 ContentLength: -1,
1115 Close: true,
1116@@ -73,7 +74,7 @@
1117 ProtoMajor: 1,
1118 ProtoMinor: 1,
1119 Request: dummyReq11("GET"),
1120- Header: Header{},
1121+ Header: http.Header{},
1122 Body: ioutil.NopCloser(strings.NewReader("abcdef")),
1123 ContentLength: -1,
1124 Close: false,
1125@@ -91,7 +92,7 @@
1126 ProtoMajor: 1,
1127 ProtoMinor: 1,
1128 Request: dummyReq11("GET"),
1129- Header: Header{},
1130+ Header: http.Header{},
1131 Body: ioutil.NopCloser(strings.NewReader("abcdef")),
1132 ContentLength: -1,
1133 TransferEncoding: []string{"chunked"},
1134@@ -108,7 +109,7 @@
1135 ProtoMajor: 1,
1136 ProtoMinor: 1,
1137 Request: dummyReq11("GET"),
1138- Header: Header{},
1139+ Header: http.Header{},
1140 Body: nil,
1141 ContentLength: 0,
1142 Close: false,
1143@@ -124,7 +125,7 @@
1144 ProtoMajor: 1,
1145 ProtoMinor: 1,
1146 Request: dummyReq11("GET"),
1147- Header: Header{},
1148+ Header: http.Header{},
1149 Body: ioutil.NopCloser(strings.NewReader("")),
1150 ContentLength: 0,
1151 Close: false,
1152@@ -140,7 +141,7 @@
1153 ProtoMajor: 1,
1154 ProtoMinor: 1,
1155 Request: dummyReq11("GET"),
1156- Header: Header{},
1157+ Header: http.Header{},
1158 Body: ioutil.NopCloser(strings.NewReader("foo")),
1159 ContentLength: 0,
1160 Close: false,
1161@@ -156,7 +157,7 @@
1273 ProtoMajor: 1,1162 ProtoMajor: 1,
1274 ProtoMinor: 1,1163 ProtoMinor: 1,
1275 Request: dummyReq("GET"),1164 Request: dummyReq("GET"),
@@ -1278,7 +1167,7 @@
1278 Body: ioutil.NopCloser(strings.NewReader("abcdef")),1167 Body: ioutil.NopCloser(strings.NewReader("abcdef")),
1279 ContentLength: 6,1168 ContentLength: 6,
1280 TransferEncoding: []string{"chunked"},1169 TransferEncoding: []string{"chunked"},
1281@@ -77,7 +78,7 @@1170@@ -177,7 +178,7 @@
1282 ProtoMajor: 1,1171 ProtoMajor: 1,
1283 ProtoMinor: 1,1172 ProtoMinor: 1,
1284 Request: dummyReq("GET"),1173 Request: dummyReq("GET"),
@@ -1287,11 +1176,20 @@
1287 "Foo": []string{" Bar\nBaz "},1176 "Foo": []string{" Bar\nBaz "},
1288 },1177 },
1289 Body: nil,1178 Body: nil,
1179@@ -200,7 +201,7 @@
1180 ProtoMajor: 1,
1181 ProtoMinor: 1,
1182 Request: &Request{Method: "POST"},
1183- Header: Header{},
1184+ Header: http.Header{},
1185 ContentLength: 0,
1186 TransferEncoding: nil,
1187 Body: nil,
12901188
1291=== modified file 'http13client/transport_test.go'1189=== modified file 'http13client/transport_test.go'
1292--- http13client/transport_test.go 2014-03-19 21:38:56 +00001190--- http13client/transport_test.go 2014-06-20 11:00:47 +0000
1293+++ http13client/transport_test.go 2014-03-19 22:27:37 +00001191+++ http13client/transport_test.go 2014-06-20 12:05:53 +0000
1294@@ -17,8 +17,8 @@1192@@ -18,8 +18,8 @@
1295 "io/ioutil"1193 "io/ioutil"
1296 "log"1194 "log"
1297 "net"1195 "net"
@@ -1301,7 +1199,7 @@
1301 "net/http/httptest"1199 "net/http/httptest"
1302 "net/url"1200 "net/url"
1303 "os"1201 "os"
1304@@ -34,7 +34,7 @@1202@@ -35,7 +35,7 @@
1305 // and then verify that the final 2 responses get errors back.1203 // and then verify that the final 2 responses get errors back.
1306 1204
1307 // hostPortHandler writes back the client's "host:port".1205 // hostPortHandler writes back the client's "host:port".
@@ -1310,7 +1208,7 @@
1310 if r.FormValue("close") == "true" {1208 if r.FormValue("close") == "true" {
1311 w.Header().Set("Connection", "close")1209 w.Header().Set("Connection", "close")
1312 }1210 }
1313@@ -280,7 +280,7 @@1211@@ -289,7 +289,7 @@
1314 const msg = "foobar"1212 const msg = "foobar"
1315 1213
1316 var addrSeen map[string]int1214 var addrSeen map[string]int
@@ -1319,7 +1217,7 @@
1319 addrSeen[r.RemoteAddr]++1217 addrSeen[r.RemoteAddr]++
1320 if r.URL.Path == "/chunked/" {1218 if r.URL.Path == "/chunked/" {
1321 w.WriteHeader(200)1219 w.WriteHeader(200)
1322@@ -299,7 +299,7 @@1220@@ -308,7 +308,7 @@
1323 wantLen := []int{len(msg), -1}[pi]1221 wantLen := []int{len(msg), -1}[pi]
1324 addrSeen = make(map[string]int)1222 addrSeen = make(map[string]int)
1325 for i := 0; i < 3; i++ {1223 for i := 0; i < 3; i++ {
@@ -1328,7 +1226,7 @@
1328 if err != nil {1226 if err != nil {
1329 t.Errorf("Get %s: %v", path, err)1227 t.Errorf("Get %s: %v", path, err)
1330 continue1228 continue
1331@@ -329,7 +329,7 @@1229@@ -338,7 +338,7 @@
1332 defer afterTest(t)1230 defer afterTest(t)
1333 resch := make(chan string)1231 resch := make(chan string)
1334 gotReq := make(chan bool)1232 gotReq := make(chan bool)
@@ -1337,7 +1235,7 @@
1337 gotReq <- true1235 gotReq <- true
1338 msg := <-resch1236 msg := <-resch
1339 _, err := w.Write([]byte(msg))1237 _, err := w.Write([]byte(msg))
1340@@ -457,12 +457,12 @@1238@@ -466,12 +466,12 @@
1341 if testing.Short() {1239 if testing.Short() {
1342 t.Skip("skipping test in short mode")1240 t.Skip("skipping test in short mode")
1343 }1241 }
@@ -1353,7 +1251,7 @@
1353 buf.Flush()1251 buf.Flush()
1354 conn.Close()1252 conn.Close()
1355 }))1253 }))
1356@@ -510,7 +510,7 @@1254@@ -519,7 +519,7 @@
1357 // with no bodies properly1255 // with no bodies properly
1358 func TestTransportHeadResponses(t *testing.T) {1256 func TestTransportHeadResponses(t *testing.T) {
1359 defer afterTest(t)1257 defer afterTest(t)
@@ -1362,7 +1260,7 @@
1362 if r.Method != "HEAD" {1260 if r.Method != "HEAD" {
1363 panic("expected HEAD; got " + r.Method)1261 panic("expected HEAD; got " + r.Method)
1364 }1262 }
1365@@ -545,7 +545,7 @@1263@@ -554,7 +554,7 @@
1366 // on responses to HEAD requests.1264 // on responses to HEAD requests.
1367 func TestTransportHeadChunkedResponse(t *testing.T) {1265 func TestTransportHeadChunkedResponse(t *testing.T) {
1368 defer afterTest(t)1266 defer afterTest(t)
@@ -1371,7 +1269,7 @@
1371 if r.Method != "HEAD" {1269 if r.Method != "HEAD" {
1372 panic("expected HEAD; got " + r.Method)1270 panic("expected HEAD; got " + r.Method)
1373 }1271 }
1374@@ -588,7 +588,7 @@1272@@ -597,7 +597,7 @@
1375 func TestRoundTripGzip(t *testing.T) {1273 func TestRoundTripGzip(t *testing.T) {
1376 defer afterTest(t)1274 defer afterTest(t)
1377 const responseBody = "test response body"1275 const responseBody = "test response body"
@@ -1380,7 +1278,7 @@
1380 accept := req.Header.Get("Accept-Encoding")1278 accept := req.Header.Get("Accept-Encoding")
1381 if expect := req.FormValue("expect_accept"); accept != expect {1279 if expect := req.FormValue("expect_accept"); accept != expect {
1382 t.Errorf("in handler, test %v: Accept-Encoding = %q, want %q",1280 t.Errorf("in handler, test %v: Accept-Encoding = %q, want %q",
1383@@ -647,7 +647,7 @@1281@@ -656,7 +656,7 @@
1384 defer afterTest(t)1282 defer afterTest(t)
1385 const testString = "The test string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"1283 const testString = "The test string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
1386 const nRandBytes = 1024 * 10241284 const nRandBytes = 1024 * 1024
@@ -1389,7 +1287,7 @@
1389 if req.Method == "HEAD" {1287 if req.Method == "HEAD" {
1390 if g := req.Header.Get("Accept-Encoding"); g != "" {1288 if g := req.Header.Get("Accept-Encoding"); g != "" {
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)
1392@@ -742,11 +742,11 @@1290@@ -751,11 +751,11 @@
1393 func TestTransportProxy(t *testing.T) {1291 func TestTransportProxy(t *testing.T) {
1394 defer afterTest(t)1292 defer afterTest(t)
1395 ch := make(chan string, 1)1293 ch := make(chan string, 1)
@@ -1403,7 +1301,7 @@
1403 ch <- "proxy for " + r.URL.String()1301 ch <- "proxy for " + r.URL.String()
1404 }))1302 }))
1405 defer proxy.Close()1303 defer proxy.Close()
1406@@ -770,7 +770,7 @@1304@@ -779,7 +779,7 @@
1407 // Content-Encoding is removed.1305 // Content-Encoding is removed.
1408 func TestTransportGzipRecursive(t *testing.T) {1306 func TestTransportGzipRecursive(t *testing.T) {
1409 defer afterTest(t)1307 defer afterTest(t)
@@ -1412,7 +1310,16 @@
1412 w.Header().Set("Content-Encoding", "gzip")1310 w.Header().Set("Content-Encoding", "gzip")
1413 w.Write(rgz)1311 w.Write(rgz)
1414 }))1312 }))
1415@@ -802,7 +802,7 @@1313@@ -807,7 +807,7 @@
1314 // a short gzip body
1315 func TestTransportGzipShort(t *testing.T) {
1316 defer afterTest(t)
1317- ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
1318+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1319 w.Header().Set("Content-Encoding", "gzip")
1320 w.Write([]byte{0x1f, 0x8b})
1321 }))
1322@@ -838,7 +838,7 @@
1416 defer afterTest(t)1323 defer afterTest(t)
1417 gotReqCh := make(chan bool)1324 gotReqCh := make(chan bool)
1418 unblockCh := make(chan bool)1325 unblockCh := make(chan bool)
@@ -1421,7 +1328,7 @@
1421 gotReqCh <- true1328 gotReqCh <- true
1422 <-unblockCh1329 <-unblockCh
1423 w.Header().Set("Content-Length", "0")1330 w.Header().Set("Content-Length", "0")
1424@@ -869,7 +869,7 @@1331@@ -905,7 +905,7 @@
1425 t.Skip("skipping test; see http://golang.org/issue/7237")1332 t.Skip("skipping test; see http://golang.org/issue/7237")
1426 }1333 }
1427 defer afterTest(t)1334 defer afterTest(t)
@@ -1430,7 +1337,7 @@
1430 }))1337 }))
1431 defer ts.Close()1338 defer ts.Close()
1432 1339
1433@@ -912,7 +912,7 @@1340@@ -948,7 +948,7 @@
1434 c := &Client{Transport: tr}1341 c := &Client{Transport: tr}
1435 1342
1436 unblockCh := make(chan bool, 1)1343 unblockCh := make(chan bool, 1)
@@ -1439,7 +1346,7 @@
1439 <-unblockCh1346 <-unblockCh
1440 tr.CloseIdleConnections()1347 tr.CloseIdleConnections()
1441 }))1348 }))
1442@@ -939,7 +939,7 @@1349@@ -975,7 +975,7 @@
1443 func TestIssue3644(t *testing.T) {1350 func TestIssue3644(t *testing.T) {
1444 defer afterTest(t)1351 defer afterTest(t)
1445 const numFoos = 50001352 const numFoos = 5000
@@ -1448,7 +1355,7 @@
1448 w.Header().Set("Connection", "close")1355 w.Header().Set("Connection", "close")
1449 for i := 0; i < numFoos; i++ {1356 for i := 0; i < numFoos; i++ {
1450 w.Write([]byte("foo "))1357 w.Write([]byte("foo "))
1451@@ -967,8 +967,8 @@1358@@ -1003,8 +1003,8 @@
1452 func TestIssue3595(t *testing.T) {1359 func TestIssue3595(t *testing.T) {
1453 defer afterTest(t)1360 defer afterTest(t)
1454 const deniedMsg = "sorry, denied."1361 const deniedMsg = "sorry, denied."
@@ -1459,7 +1366,7 @@
1459 }))1366 }))
1460 defer ts.Close()1367 defer ts.Close()
1461 tr := &Transport{}1368 tr := &Transport{}
1462@@ -991,7 +991,7 @@1369@@ -1027,7 +1027,7 @@
1463 // "client fails to handle requests with no body and chunked encoding"1370 // "client fails to handle requests with no body and chunked encoding"
1464 func TestChunkedNoContent(t *testing.T) {1371 func TestChunkedNoContent(t *testing.T) {
1465 defer afterTest(t)1372 defer afterTest(t)
@@ -1468,7 +1375,7 @@
1468 w.WriteHeader(StatusNoContent)1375 w.WriteHeader(StatusNoContent)
1469 }))1376 }))
1470 defer ts.Close()1377 defer ts.Close()
1471@@ -1019,7 +1019,7 @@1378@@ -1055,7 +1055,7 @@
1472 maxProcs, numReqs = 4, 501379 maxProcs, numReqs = 4, 50
1473 }1380 }
1474 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs))1381 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs))
@@ -1477,7 +1384,7 @@
1477 fmt.Fprintf(w, "%v", r.FormValue("echo"))1384 fmt.Fprintf(w, "%v", r.FormValue("echo"))
1478 }))1385 }))
1479 defer ts.Close()1386 defer ts.Close()
1480@@ -1080,8 +1080,8 @@1387@@ -1116,8 +1116,8 @@
1481 }1388 }
1482 defer afterTest(t)1389 defer afterTest(t)
1483 const debug = false1390 const debug = false
@@ -1488,7 +1395,7 @@
1488 io.Copy(w, neverEnding('a'))1395 io.Copy(w, neverEnding('a'))
1489 })1396 })
1490 ts := httptest.NewServer(mux)1397 ts := httptest.NewServer(mux)
1491@@ -1144,11 +1144,11 @@1398@@ -1180,11 +1180,11 @@
1492 }1399 }
1493 defer afterTest(t)1400 defer afterTest(t)
1494 const debug = false1401 const debug = false
@@ -1503,20 +1410,22 @@
1503 defer r.Body.Close()1410 defer r.Body.Close()
1504 io.Copy(ioutil.Discard, r.Body)1411 io.Copy(ioutil.Discard, r.Body)
1505 })1412 })
1506@@ -1214,9 +1214,9 @@1413@@ -1251,11 +1251,11 @@
1507 if testing.Short() {
1508 t.Skip("skipping timeout test in -short mode")1414 t.Skip("skipping timeout test in -short mode")
1509 }1415 }
1416 inHandler := make(chan bool, 1)
1510- mux := NewServeMux()1417- mux := NewServeMux()
1511- mux.HandleFunc("/fast", func(w ResponseWriter, r *Request) {})1418- mux.HandleFunc("/fast", func(w ResponseWriter, r *Request) {
1419+ mux := http.NewServeMux()
1420+ mux.HandleFunc("/fast", func(w http.ResponseWriter, r *http.Request) {
1421 inHandler <- true
1422 })
1512- mux.HandleFunc("/slow", func(w ResponseWriter, r *Request) {1423- mux.HandleFunc("/slow", func(w ResponseWriter, r *Request) {
1513+ mux := http.NewServeMux()
1514+ mux.HandleFunc("/fast", func(w http.ResponseWriter, r *http.Request) {})
1515+ mux.HandleFunc("/slow", func(w http.ResponseWriter, r *http.Request) {1424+ mux.HandleFunc("/slow", func(w http.ResponseWriter, r *http.Request) {
1425 inHandler <- true
1516 time.Sleep(2 * time.Second)1426 time.Sleep(2 * time.Second)
1517 })1427 })
1518 ts := httptest.NewServer(mux)1428@@ -1322,9 +1322,9 @@
1519@@ -1276,9 +1276,9 @@
1520 t.Skip("skipping test in -short mode")1429 t.Skip("skipping test in -short mode")
1521 }1430 }
1522 unblockc := make(chan bool)1431 unblockc := make(chan bool)
@@ -1528,7 +1437,7 @@
1528 <-unblockc1437 <-unblockc
1529 }))1438 }))
1530 defer ts.Close()1439 defer ts.Close()
1531@@ -1386,14 +1386,14 @@1440@@ -1431,14 +1431,14 @@
1532 defer afterTest(t)1441 defer afterTest(t)
1533 writeErr := make(chan error, 1)1442 writeErr := make(chan error, 1)
1534 msg := []byte("young\n")1443 msg := []byte("young\n")
@@ -1545,7 +1454,7 @@
1545 }1454 }
1546 }))1455 }))
1547 defer ts.Close()1456 defer ts.Close()
1548@@ -1449,7 +1449,7 @@1457@@ -1494,7 +1494,7 @@
1549 res := &Response{1458 res := &Response{
1550 Status: "200 OK",1459 Status: "200 OK",
1551 StatusCode: 200,1460 StatusCode: 200,
@@ -1554,7 +1463,7 @@
1554 Body: ioutil.NopCloser(strings.NewReader("You wanted " + req.URL.String())),1463 Body: ioutil.NopCloser(strings.NewReader("You wanted " + req.URL.String())),
1555 }1464 }
1556 return res, nil1465 return res, nil
1557@@ -1478,7 +1478,7 @@1466@@ -1523,7 +1523,7 @@
1558 defer afterTest(t)1467 defer afterTest(t)
1559 tr := &Transport{}1468 tr := &Transport{}
1560 _, err := tr.RoundTrip(&Request{1469 _, err := tr.RoundTrip(&Request{
@@ -1563,7 +1472,7 @@
1563 URL: &url.URL{1472 URL: &url.URL{
1564 Scheme: "http",1473 Scheme: "http",
1565 },1474 },
1566@@ -1492,14 +1492,14 @@1475@@ -1537,14 +1537,14 @@
1567 func TestTransportSocketLateBinding(t *testing.T) {1476 func TestTransportSocketLateBinding(t *testing.T) {
1568 defer afterTest(t)1477 defer afterTest(t)
1569 1478
@@ -1582,7 +1491,7 @@
1582 w.Header().Set("bar-ipport", r.RemoteAddr)1491 w.Header().Set("bar-ipport", r.RemoteAddr)
1583 })1492 })
1584 ts := httptest.NewServer(mux)1493 ts := httptest.NewServer(mux)
1585@@ -1720,7 +1720,7 @@1494@@ -1767,7 +1767,7 @@
1586 var mu sync.Mutex1495 var mu sync.Mutex
1587 var n int1496 var n int
1588 1497
@@ -1591,7 +1500,7 @@
1591 mu.Lock()1500 mu.Lock()
1592 n++1501 n++
1593 mu.Unlock()1502 mu.Unlock()
1594@@ -1756,7 +1756,7 @@1503@@ -1803,7 +1803,7 @@
1595 // then closes it.1504 // then closes it.
1596 func TestTransportClosesRequestBody(t *testing.T) {1505 func TestTransportClosesRequestBody(t *testing.T) {
1597 defer afterTest(t)1506 defer afterTest(t)
@@ -1600,4 +1509,49 @@
1600 io.Copy(ioutil.Discard, r.Body)1509 io.Copy(ioutil.Discard, r.Body)
1601 }))1510 }))
1602 defer ts.Close()1511 defer ts.Close()
1512@@ -1890,9 +1890,9 @@
1513 t.Skip("skipping flaky test on Windows; golang.org/issue/7634")
1514 }
1515 closedc := make(chan bool, 1)
1516- ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
1517+ ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1518 if strings.Contains(r.URL.Path, "/keep-alive-then-die") {
1519- conn, _, _ := w.(Hijacker).Hijack()
1520+ conn, _, _ := w.(http.Hijacker).Hijack()
1521 conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nfoo"))
1522 conn.Close()
1523 closedc <- true
1524@@ -1994,12 +1994,12 @@
1525 }
1526 defer closeConn()
1527
1528- ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
1529+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1530 if r.Method == "GET" {
1531 io.WriteString(w, "bar")
1532 return
1533 }
1534- conn, _, _ := w.(Hijacker).Hijack()
1535+ conn, _, _ := w.(http.Hijacker).Hijack()
1536 sconn.Lock()
1537 sconn.c = conn
1538 sconn.Unlock()
1539@@ -2056,7 +2056,7 @@
1540 }
1541 defer afterTest(t)
1542 readBody := make(chan error, 1)
1543- ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
1544+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1545 _, err := ioutil.ReadAll(r.Body)
1546 readBody <- err
1547 }))
1548@@ -2098,7 +2098,7 @@
1549 }
1550 }
1551
1552-func wantBody(res *http.Response, err error, want string) error {
1553+func wantBody(res *Response, err error, want string) error {
1554 if err != nil {
1555 return err
1556 }
16031557
16041558
=== modified file 'http13client/_using.txt'
--- http13client/_using.txt 2014-03-20 12:20:01 +0000
+++ http13client/_using.txt 2014-07-02 13:12:36 +0000
@@ -1,5 +1,5 @@
1parent: 19512:32c32aef2a41 tip1parent: 20169:9895f9e36435 go1.3 release
2 test: enable bug385_32 test on amd64p32.2 go1.3
3branch: default3branch: release-branch.go1.3
4commit: (clean)4commit: (clean)
5update: (current)5update: (current)
66
=== modified file 'http13client/client.go'
--- http13client/client.go 2014-03-20 09:26:28 +0000
+++ http13client/client.go 2014-07-02 13:12:36 +0000
@@ -92,8 +92,9 @@
92 // authentication, or cookies.92 // authentication, or cookies.
93 //93 //
94 // RoundTrip should not modify the request, except for94 // RoundTrip should not modify the request, except for
95 // consuming and closing the Body. The request's URL and95 // consuming and closing the Body, including on errors. The
96 // Header fields are guaranteed to be initialized.96 // request's URL and Header fields are guaranteed to be
97 // initialized.
97 RoundTrip(*Request) (*Response, error)98 RoundTrip(*Request) (*Response, error)
98}99}
99100
@@ -141,6 +142,9 @@
141// (typically Transport) may not be able to re-use a persistent TCP142// (typically Transport) may not be able to re-use a persistent TCP
142// connection to the server for a subsequent "keep-alive" request.143// connection to the server for a subsequent "keep-alive" request.
143//144//
145// The request Body, if non-nil, will be closed by the underlying
146// Transport, even on errors.
147//
144// Generally Get, Post, or PostForm will be used instead of Do.148// Generally Get, Post, or PostForm will be used instead of Do.
145func (c *Client) Do(req *Request) (resp *Response, err error) {149func (c *Client) Do(req *Request) (resp *Response, err error) {
146 if req.Method == "GET" || req.Method == "HEAD" {150 if req.Method == "GET" || req.Method == "HEAD" {
@@ -163,14 +167,17 @@
163// Caller should close resp.Body when done reading from it.167// Caller should close resp.Body when done reading from it.
164func send(req *Request, t RoundTripper) (resp *Response, err error) {168func send(req *Request, t RoundTripper) (resp *Response, err error) {
165 if t == nil {169 if t == nil {
170 req.closeBody()
166 return nil, errors.New("http: no Client.Transport or DefaultTransport")171 return nil, errors.New("http: no Client.Transport or DefaultTransport")
167 }172 }
168173
169 if req.URL == nil {174 if req.URL == nil {
175 req.closeBody()
170 return nil, errors.New("http: nil Request.URL")176 return nil, errors.New("http: nil Request.URL")
171 }177 }
172178
173 if req.RequestURI != "" {179 if req.RequestURI != "" {
180 req.closeBody()
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.")
175 }182 }
176183
@@ -278,6 +285,7 @@
278 var via []*Request285 var via []*Request
279286
280 if ireq.URL == nil {287 if ireq.URL == nil {
288 ireq.closeBody()
281 return nil, errors.New("http: nil Request.URL")289 return nil, errors.New("http: nil Request.URL")
282 }290 }
283291
@@ -400,7 +408,7 @@
400// Caller should close resp.Body when done reading from it.408// Caller should close resp.Body when done reading from it.
401//409//
402// If the provided body is also an io.Closer, it is closed after the410// If the provided body is also an io.Closer, it is closed after the
403// body is successfully written to the server.411// request.
404func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {412func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
405 req, err := NewRequest("POST", url, body)413 req, err := NewRequest("POST", url, body)
406 if err != nil {414 if err != nil {
407415
=== modified file 'http13client/client_test.go'
--- http13client/client_test.go 2014-03-20 09:26:28 +0000
+++ http13client/client_test.go 2014-07-02 13:12:36 +0000
@@ -20,6 +20,8 @@
20 "net/http"20 "net/http"
21 "net/http/httptest"21 "net/http/httptest"
22 "net/url"22 "net/url"
23 "reflect"
24 "sort"
23 "strconv"25 "strconv"
24 "strings"26 "strings"
25 "sync"27 "sync"
@@ -877,3 +879,93 @@
877 t.Fatal("server saw different client ports before & after the redirect")879 t.Fatal("server saw different client ports before & after the redirect")
878 }880 }
879}881}
882
883// eofReaderFunc is an io.Reader that runs itself, and then returns io.EOF.
884type eofReaderFunc func()
885
886func (f eofReaderFunc) Read(p []byte) (n int, err error) {
887 f()
888 return 0, io.EOF
889}
890
891func TestClientTrailers(t *testing.T) {
892 defer afterTest(t)
893 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
894 w.Header().Set("Connection", "close")
895 w.Header().Set("Trailer", "Server-Trailer-A, Server-Trailer-B")
896 w.Header().Add("Trailer", "Server-Trailer-C")
897
898 var decl []string
899 for k := range r.Trailer {
900 decl = append(decl, k)
901 }
902 sort.Strings(decl)
903
904 slurp, err := ioutil.ReadAll(r.Body)
905 if err != nil {
906 t.Errorf("Server reading request body: %v", err)
907 }
908 if string(slurp) != "foo" {
909 t.Errorf("Server read request body %q; want foo", slurp)
910 }
911 if r.Trailer == nil {
912 io.WriteString(w, "nil Trailer")
913 } else {
914 fmt.Fprintf(w, "decl: %v, vals: %s, %s",
915 decl,
916 r.Trailer.Get("Client-Trailer-A"),
917 r.Trailer.Get("Client-Trailer-B"))
918 }
919
920 // TODO: golang.org/issue/7759: there's no way yet for
921 // the server to set trailers without hijacking, so do
922 // that for now, just to test the client. Later, in
923 // Go 1.4, it should be implicit that any mutations
924 // to w.Header() after the initial write are the
925 // trailers to be sent, if and only if they were
926 // previously declared with w.Header().Set("Trailer",
927 // ..keys..)
928 w.(http.Flusher).Flush()
929 conn, buf, _ := w.(http.Hijacker).Hijack()
930 t := http.Header{}
931 t.Set("Server-Trailer-A", "valuea")
932 t.Set("Server-Trailer-C", "valuec") // skipping B
933 buf.WriteString("0\r\n") // eof
934 t.Write(buf)
935 buf.WriteString("\r\n") // end of trailers
936 buf.Flush()
937 conn.Close()
938 }))
939 defer ts.Close()
940
941 var req *Request
942 req, _ = NewRequest("POST", ts.URL, io.MultiReader(
943 eofReaderFunc(func() {
944 req.Trailer["Client-Trailer-A"] = []string{"valuea"}
945 }),
946 strings.NewReader("foo"),
947 eofReaderFunc(func() {
948 req.Trailer["Client-Trailer-B"] = []string{"valueb"}
949 }),
950 ))
951 req.Trailer = http.Header{
952 "Client-Trailer-A": nil, // to be set later
953 "Client-Trailer-B": nil, // to be set later
954 }
955 req.ContentLength = -1
956 res, err := DefaultClient.Do(req)
957 if err != nil {
958 t.Fatal(err)
959 }
960 if err := wantBody(res, err, "decl: [Client-Trailer-A Client-Trailer-B], vals: valuea, valueb"); err != nil {
961 t.Error(err)
962 }
963 want := http.Header{
964 "Server-Trailer-A": []string{"valuea"},
965 "Server-Trailer-B": nil,
966 "Server-Trailer-C": []string{"valuec"},
967 }
968 if !reflect.DeepEqual(res.Trailer, want) {
969 t.Errorf("Response trailers = %#v; want %#v", res.Trailer, want)
970 }
971}
880972
=== modified file 'http13client/cookie.go'
--- http13client/cookie.go 2014-03-19 23:13:58 +0000
+++ http13client/cookie.go 2014-07-02 13:12:36 +0000
@@ -55,11 +55,7 @@
55 attr, val = attr[:j], attr[j+1:]55 attr, val = attr[:j], attr[j+1:]
56 }56 }
57 lowerAttr := strings.ToLower(attr)57 lowerAttr := strings.ToLower(attr)
58 parseCookieValueFn := parseCookieValue58 val, success = parseCookieValue(val)
59 if lowerAttr == "expires" {
60 parseCookieValueFn = parseCookieExpiresValue
61 }
62 val, success = parseCookieValueFn(val)
63 if !success {59 if !success {
64 c.Unparsed = append(c.Unparsed, parts[i])60 c.Unparsed = append(c.Unparsed, parts[i])
65 continue61 continue
@@ -230,12 +226,23 @@
230// ; US-ASCII characters excluding CTLs,226// ; US-ASCII characters excluding CTLs,
231// ; whitespace DQUOTE, comma, semicolon,227// ; whitespace DQUOTE, comma, semicolon,
232// ; and backslash228// ; and backslash
229// We loosen this as spaces and commas are common in cookie values
230// but we produce a quoted cookie-value in when value starts or ends
231// with a comma or space.
232// See http://golang.org/issue/7243 for the discussion.
233func sanitizeCookieValue(v string) string {233func sanitizeCookieValue(v string) string {
234 return sanitizeOrWarn("Cookie.Value", validCookieValueByte, v)234 v = sanitizeOrWarn("Cookie.Value", validCookieValueByte, v)
235 if len(v) == 0 {
236 return v
237 }
238 if v[0] == ' ' || v[0] == ',' || v[len(v)-1] == ' ' || v[len(v)-1] == ',' {
239 return `"` + v + `"`
240 }
241 return v
235}242}
236243
237func validCookieValueByte(b byte) bool {244func validCookieValueByte(b byte) bool {
238 return 0x20 < b && b < 0x7f && b != '"' && b != ',' && b != ';' && b != '\\'245 return 0x20 <= b && b < 0x7f && b != '"' && b != ';' && b != '\\'
239}246}
240247
241// path-av = "Path=" path-value248// path-av = "Path=" path-value
@@ -270,38 +277,13 @@
270 return string(buf)277 return string(buf)
271}278}
272279
273func unquoteCookieValue(v string) string {
274 if len(v) > 1 && v[0] == '"' && v[len(v)-1] == '"' {
275 return v[1 : len(v)-1]
276 }
277 return v
278}
279
280func isCookieByte(c byte) bool {
281 switch {
282 case c == 0x21, 0x23 <= c && c <= 0x2b, 0x2d <= c && c <= 0x3a,
283 0x3c <= c && c <= 0x5b, 0x5d <= c && c <= 0x7e:
284 return true
285 }
286 return false
287}
288
289func isCookieExpiresByte(c byte) (ok bool) {
290 return isCookieByte(c) || c == ',' || c == ' '
291}
292
293func parseCookieValue(raw string) (string, bool) {280func parseCookieValue(raw string) (string, bool) {
294 return parseCookieValueUsing(raw, isCookieByte)281 // Strip the quotes, if present.
295}282 if len(raw) > 1 && raw[0] == '"' && raw[len(raw)-1] == '"' {
296283 raw = raw[1 : len(raw)-1]
297func parseCookieExpiresValue(raw string) (string, bool) {284 }
298 return parseCookieValueUsing(raw, isCookieExpiresByte)
299}
300
301func parseCookieValueUsing(raw string, validByte func(byte) bool) (string, bool) {
302 raw = unquoteCookieValue(raw)
303 for i := 0; i < len(raw); i++ {285 for i := 0; i < len(raw); i++ {
304 if !validByte(raw[i]) {286 if !validCookieValueByte(raw[i]) {
305 return "", false287 return "", false
306 }288 }
307 }289 }
308290
=== removed file 'http13client/cookie_test.go'
--- http13client/cookie_test.go 2014-03-19 23:13:58 +0000
+++ http13client/cookie_test.go 1970-01-01 00:00:00 +0000
@@ -1,304 +0,0 @@
1// Copyright 2010 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package http
6
7import (
8 "bytes"
9 "encoding/json"
10 "fmt"
11 "log"
12 "net/http"
13 "os"
14 "reflect"
15 "strings"
16 "testing"
17 "time"
18)
19
20var writeSetCookiesTests = []struct {
21 Cookie *http.Cookie
22 Raw string
23}{
24 {
25 &http.Cookie{Name: "cookie-1", Value: "v$1"},
26 "cookie-1=v$1",
27 },
28 {
29 &http.Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600},
30 "cookie-2=two; Max-Age=3600",
31 },
32 {
33 &http.Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"},
34 "cookie-3=three; Domain=example.com",
35 },
36 {
37 &http.Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"},
38 "cookie-4=four; Path=/restricted/",
39 },
40 {
41 &http.Cookie{Name: "cookie-5", Value: "five", Domain: "wrong;bad.abc"},
42 "cookie-5=five",
43 },
44 {
45 &http.Cookie{Name: "cookie-6", Value: "six", Domain: "bad-.abc"},
46 "cookie-6=six",
47 },
48 {
49 &http.Cookie{Name: "cookie-7", Value: "seven", Domain: "127.0.0.1"},
50 "cookie-7=seven; Domain=127.0.0.1",
51 },
52 {
53 &http.Cookie{Name: "cookie-8", Value: "eight", Domain: "::1"},
54 "cookie-8=eight",
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches