diff --git a/README.md b/README.md index c961ddf..b0dc56a 100644 --- a/README.md +++ b/README.md @@ -184,19 +184,6 @@ if e, ok := err.(*push.Error); ok { } ``` -##### GOAWAY errors - -You will never receive some of Apple's documented error responses, such as `BadCertificateEnvironment`. This is because Apple sometimes returns error messages in a GOAWAY frame while disconnecting rather than in the usual DATA frame. Go [doesn't currently](https://github.com/golang/go/issues/14627) extract these GOAWAY errors. Instead you may see an error like this: - -> http2: server sent GOAWAY and closed the connection - -You can run your application with HTTP/2 logging enabled to see the GOAWAY frames that were sent, including the reason. - -```console -$ GODEBUG=http2debug=1 ./myapp -http2: Transport received GOAWAY len=46 LastStreamID=0 ErrCode=NO_ERROR Debug="{\"reason\":\"BadCertificateEnvironment\"}" -``` - ### Website Push Before you can send push notifications through Safari and the Notification Center, you must provide a push package, which is a signed zip file containing some JSON and icons. diff --git a/push/service.go b/push/service.go index 304ab5a..a1d7c4d 100644 --- a/push/service.go +++ b/push/service.go @@ -7,7 +7,9 @@ import ( "crypto/tls" "encoding/json" "fmt" + "io" "net/http" + "strings" "time" "golang.org/x/net/http2" @@ -71,8 +73,16 @@ func (s *Service) Push(deviceToken string, headers *Headers, payload []byte) (st req.Header.Set("Content-Type", "application/json") headers.set(req.Header) - resp, err := s.Client.Do(req) + tr := s.Client.Transport + if tr == nil { + tr = http.DefaultTransport + } + resp, err := tr.RoundTrip(req) if err != nil { + if e, ok := err.(http2.GoAwayError); ok { + // parse DebugData as JSON. no status code known (0) + return "", parseErrorResponse(strings.NewReader(e.DebugData), 0) + } return "", err } defer resp.Body.Close() @@ -81,26 +91,29 @@ func (s *Service) Push(deviceToken string, headers *Headers, payload []byte) (st return resp.Header.Get("apns-id"), nil } + return "", parseErrorResponse(resp.Body, resp.StatusCode) +} + +func parseErrorResponse(body io.Reader, statusCode int) error { var response struct { // Reason for failure Reason string `json:"reason"` // Timestamp for 410 StatusGone (ErrUnregistered) Timestamp int64 `json:"timestamp"` } - err = json.NewDecoder(resp.Body).Decode(&response) + err := json.NewDecoder(body).Decode(&response) if err != nil { - return "", err + return err } es := &Error{ Reason: mapErrorReason(response.Reason), - Status: resp.StatusCode, + Status: statusCode, } if response.Timestamp != 0 { // the response.Timestamp is Milliseconds, but time.Unix() requires seconds es.Timestamp = time.Unix(response.Timestamp/1000, 0).UTC() } - - return "", es + return es }