func (self *admPushService) Push(psp *push.PushServiceProvider, dpQueue <-chan *push.DeliveryPoint, resQueue chan<- *push.PushResult, notif *push.Notification) { defer close(resQueue) defer func() { for _ = range dpQueue { } }() res := new(push.PushResult) res.Content = notif res.Provider = psp var err push.PushError psp, err = self.lockPsp(psp) if err != nil { res.Err = err resQueue <- res if _, ok := err.(*push.PushServiceProviderUpdate); !ok { return } } data, err := self.notifToJSON(notif) if err != nil { res.Err = err resQueue <- res return } wg := sync.WaitGroup{} for dp := range dpQueue { wg.Add(1) res := new(push.PushResult) res.Content = notif res.Provider = psp res.Destination = dp go func(dp *push.DeliveryPoint) { res.MsgId, res.Err = admSinglePush(psp, dp, data, notif) resQueue <- res wg.Done() }(dp) } wg.Wait() }
// Push will read all of the delivery points to send to from dpQueue and send responses on resQueue before closing the channel. If the notification data is invalid, // it will send only one response. func (self *pushService) Push(psp *push.PushServiceProvider, dpQueue <-chan *push.DeliveryPoint, resQueue chan<- *push.PushResult, notif *push.Notification) { defer close(resQueue) // Profiling // self.updateCheckPoint("") var err push.PushError req := new(common.PushRequest) req.PSP = psp req.Payload, err = toAPNSPayload(notif) if err == nil && len(req.Payload) > self.requestProcessor.GetMaxPayloadSize() { err = push.NewBadNotificationWithDetails(fmt.Sprintf("payload is too large: %d > %d", len(req.Payload), self.requestProcessor.GetMaxPayloadSize())) } if err != nil { res := new(push.PushResult) res.Provider = psp res.Content = notif res.Err = push.NewErrorf("Failed to create push: %v", err) resQueue <- res for _ = range dpQueue { } return } unixNow := uint32(time.Now().Unix()) expiry := unixNow + 60*60 if ttlstr, ok := notif.Data["ttl"]; ok { ttl, err := strconv.ParseUint(ttlstr, 10, 32) if err == nil { expiry = unixNow + uint32(ttl) } } req.Expiry = expiry req.Devtokens = make([][]byte, 0, 10) dpList := make([]*push.DeliveryPoint, 0, 10) for dp := range dpQueue { res := new(push.PushResult) res.Destination = dp res.Provider = psp res.Content = notif devtoken, ok := dp.FixedData["devtoken"] if !ok { res.Err = push.NewBadDeliveryPointWithDetails(dp, "NoDevtoken") resQueue <- res continue } btoken, err := hex.DecodeString(devtoken) if err != nil { res.Err = push.NewBadDeliveryPointWithDetails(dp, err.Error()) resQueue <- res continue } req.Devtokens = append(req.Devtokens, btoken) dpList = append(dpList, dp) } n := len(req.Devtokens) lastId := self.getMessageIds(n) req.MaxMsgId = lastId req.DPList = dpList // We send this request object to be processed by pushMux goroutine, to send responses/errors back. errChan := make(chan push.PushError) resChan := make(chan *common.APNSResult, n) req.ErrChan = errChan req.ResChan = resChan self.requestProcessor.AddRequest(req) // errChan closed means the message(s) is/are sent successfully to the APNs. // However, we may have not yet receieved responses from APNS - those are sent on resChan for err = range errChan { res := new(push.PushResult) res.Provider = psp res.Content = notif if _, ok := err.(*push.ErrorReport); ok { res.Err = push.NewErrorf("Failed to send payload to APNS: %v", err) } else { res.Err = err } resQueue <- res } // Profiling // self.updateCheckPoint("sending the message takes") if err != nil { return } for i, dp := range dpList { if dp != nil { r := new(push.PushResult) r.Provider = psp r.Content = notif r.Destination = dp mid := req.GetId(i) r.MsgId = fmt.Sprintf("apns:%v-%v", psp.Name(), mid) r.Err = nil resQueue <- r } } // Wait for the unserialized responses from APNS asynchronously - these will not affect what we send our clients for this request, but will affect subsequent requests. go self.waitResults(psp, dpList, lastId, resChan) }