예제 #1
0
파일: adm.go 프로젝트: uniqush/uniqush-push
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()
}
예제 #2
0
// 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)
}