anachronistic/apns — pull request #14 by draaglom (2014-04-30 19:05:36+00:00)
Title: Persistent connections

Initial main structure of a persistent connection; completely non-functional as of now.

What's changed:
- PushNotification.Identifier is now an uint32; it means we can use the whole identifier range. This also means that IDENTIFIER_UBOUND is no longer needed.
- A new Response type is defined which maps directly to the apple response

What's added:
- There's now a Connection, which is backed by a simple system of goroutines connected by queues. The way it will work is this:
A sender goroutine runs through the push notification queue, (connects to APNS if the conection is down) and passes the notifications on to the sent queue.
A reader goroutine blocks waiting for a response from APNS and passes it on and closes the connection.
A 'limbo' goroutine keeps a queue of sent notifications, and discards / requeues notifications when it receives responses.
It also sends "success" responses to itself every now and then, to flush out the notifications which have been around longer than TimeoutSeconds.

Things I still need to implement:
- Actually connecting to APNS
- Actually sending notifications
- The sender should probably exponentially backoff if it can't reconnect (up to some limit)
- Documentation
- Tests
- Probably lots more things

Things I still need to think about:
- The reader goroutine might need to be spawned each time the connection is started rather than running on (because what happens when the connection is down?
(it can't connect itself, that's the sender's responsibility)
- Now that the sender routine is responsible for reconnecting itself each time it disconnects, the user doesn't have direct access to the state of the connection.
That is to say, if for some reason the sender is unable to reconnect for an extended period of time (say, in a network outage)
the notifications will just pile up and you won't notice right away that they're not being sent.
On the one hand we could say "this is really bad" and add another error queue for connection errors; but how can the user reasonably deal with them anyway?
Since avoidable errors (bad configuration, etc) would probably be caught in Start() we can probably just log'em and hope?
- Graceful shutdown feels complicated because of the channels going in a loop. If a goroutine doesn't know its input stream is finished, it can't close its downstream channels...