Created
June 14, 2013 02:39
-
-
Save mash/5779104 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package AnyEvent::APNS::FeedbackService; | |
| use utf8; | |
| use Mouse; | |
| use AnyEvent 4.80; | |
| use AnyEvent::Handle; | |
| use AnyEvent::Socket; | |
| use AnyEvent::TLS; | |
| our $VERSION = '0.01'; | |
| has certificate => ( | |
| is => 'rw', | |
| isa => 'Str | ScalarRef', | |
| required => 1, | |
| ); | |
| has private_key => ( | |
| is => 'rw', | |
| isa => 'Str | ScalarRef', | |
| required => 1, | |
| ); | |
| has sandbox => ( | |
| is => 'rw', | |
| isa => 'Bool', | |
| default => 0, | |
| ); | |
| has handler => ( | |
| is => 'rw', | |
| isa => 'AnyEvent::Handle', | |
| predicate => 'connected', | |
| clearer => 'clear_handler', | |
| ); | |
| has on_error => ( | |
| is => 'rw', | |
| isa => 'CodeRef', | |
| default => sub { sub { warn @_ } }, | |
| ); | |
| has on_connect => ( | |
| is => 'rw', | |
| isa => 'CodeRef', | |
| default => sub { sub {} }, | |
| ); | |
| has on_received_device_token => ( | |
| is => 'rw', | |
| isa => 'CodeRef', | |
| default => sub { | |
| sub { | |
| my ($data) = @_; | |
| warn "$data->{ time } $data->{ device_token }"; | |
| }; | |
| }, | |
| ); | |
| has debug_port => ( | |
| is => 'rw', | |
| isa => 'Int', | |
| predicate => 'is_debug', | |
| ); | |
| no Mouse; | |
| sub connect { | |
| my $self = shift; | |
| if ($self->connected && $self->handler) { | |
| warn 'Already connected!'; | |
| return; | |
| } | |
| my $host = $self->sandbox | |
| ? 'feedback.sandbox.push.apple.com' | |
| : 'feedback.push.apple.com'; | |
| my $port = 2196; | |
| if ($self->is_debug) { | |
| $host = '127.0.0.1'; | |
| $port = $self->debug_port; | |
| } | |
| my $g; $g = tcp_connect $host, $port, sub { | |
| my ($fh) = @_ | |
| or return $self->on_error->(undef, 1, $!); | |
| my $tls_setting = {}; | |
| if (ref $self->certificate) { | |
| $tls_setting->{cert} = ${ $self->certificate }; | |
| } | |
| else { | |
| $tls_setting->{cert_file} = $self->certificate; | |
| } | |
| if (ref $self->private_key) { | |
| $tls_setting->{key} = ${ $self->private_key }; | |
| } | |
| else { | |
| $tls_setting->{key_file} = $self->private_key; | |
| } | |
| my $handle = AnyEvent::Handle->new( | |
| fh => $fh, | |
| on_error => sub { | |
| # apple just disconnects immediately after sending their device_tokens list | |
| # we receive a "Broken pipe" error | |
| $self->on_error->(@_); | |
| $self->clear_handler; | |
| $_[0]->destroy; | |
| undef $g; | |
| }, | |
| on_read => sub { | |
| $self->_on_read( @_ ); | |
| }, | |
| !$self->is_debug ? ( | |
| tls => 'connect', | |
| tls_ctx => $tls_setting, | |
| ) : (), | |
| ); | |
| $self->handler( $handle ); | |
| $self->on_connect->(); | |
| }; | |
| Scalar::Util::weaken($self); | |
| $self; | |
| } | |
| sub _on_read { | |
| my ($self, $handle) = @_; | |
| $handle->push_read( | |
| chunk => 36, | |
| sub { | |
| my $time = unpack( 'N', substr( $_[1], 0, 4) ); # N: big endian long | |
| my $length = unpack( 'n', substr( $_[1], 4, 2) ); # n: big endian short | |
| my $device_token = unpack( 'H*', substr( $_[1], 6, 32 ) ); # hex | |
| $self->on_received_device_token->({ | |
| time => $time, | |
| device_token => $device_token, | |
| }); | |
| }); | |
| } | |
| 1; | |
| __END__ | |
| =for stopwords | |
| apns feedbackservice iPhone multi-byte utf8 | |
| =head1 NAME | |
| AnyEvent::APNS::FeedbackService - Simple wrapper for Apple Push Notifications Service (APNS) feedback service | |
| =head1 SYNOPSIS | |
| use AnyEvent::APNS::FeedbackService; | |
| my $cv = AnyEvent->condvar; | |
| my $apns; $apns = AnyEvent::APNS::FeedbackService->new( | |
| certificate => 'your apns certificate file', | |
| private_key => 'your apns private key file', | |
| sandbox => 1, | |
| on_error => sub { | |
| warn @_; | |
| $cv->send; # no more device_tokens list | |
| }, | |
| on_connect => sub {}, | |
| on_received_device_token => sub { | |
| my ($data) = @_; | |
| warn "$data->{ time } $data->{ device_token }"; | |
| }, | |
| ); | |
| $apns->connect; | |
| $cv->recv; | |
| =head1 DESCRIPTION | |
| This module helps you to create Apple Push Notifications Service (APNS) Feedback Service | |
| =head1 METHODS | |
| =head2 new | |
| Create APNS Feedback Service object. | |
| my $apns = AnyEvent::APNS::FeedbackService->new( | |
| certificate => 'your apns certificate file', | |
| private_key => 'your apns private key file', | |
| sandbox => 1, | |
| on_error => sub {}, | |
| ); | |
| Supported arguments are: | |
| =over 4 | |
| =item certificate => 'Str | ScalarRef' | |
| certificate => '/path/to/certificate_file', | |
| # or | |
| certificate => \$certificate, | |
| Required. Either file path for certificate or scalar-ref of certificate data. | |
| =item private_key => 'Str | ScalarRef' | |
| private_key => '/path/to/private_key', | |
| # or | |
| private_key => \$private_key, | |
| Required. Either file path for private_key or scalar-ref of private-key data. | |
| =item sandbox => 0|1 | |
| This is a flag indicate target service is provisioning (sandbox => 1) or distribution (sandbox => 0) | |
| Optional (Default: 0) | |
| =item on_error => $cb->($handle, $fatal, $message) | |
| Callback to be called when something error occurs. | |
| This is wrapper for L<AnyEvent::Handle>'s on_error callbacks. Look at the document for more detail. | |
| Optional (Default: just warn error) | |
| =item on_connect => $cb->() | |
| Callback to be called when connection established to apns server. | |
| Optional (Default: empty coderef) | |
| =back | |
| =head2 $apns->connect; | |
| Connect to apns server. | |
| =head2 $apns->handler | |
| Return L<AnyEvent::Handle> object which is used to current established connection. It returns undef before connection completed. | |
| =head1 TODO | |
| =over 4 | |
| =item * | |
| More correct error handling | |
| =back | |
| =head1 AUTHOR | |
| Masakazu Ohtsuka <[email protected]> | |
| =head1 COPYRIGHT AND LICENSE | |
| This program is free software; you can redistribute | |
| it and/or modify it under the same terms as Perl itself. | |
| The full text of the license can be found in the | |
| LICENSE file included with this module. | |
| =cut |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment