File Coverage

lib/HTTP/Promise/Stream/Base64.pm
Criterion Covered Total %
statement 72 80 90.0
branch 12 24 50.0
condition 2 6 33.3
subroutine 14 16 87.5
pod 6 6 100.0
total 106 132 80.3


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## Asynchronous HTTP Request and Promise - ~/lib/HTTP/Promise/Stream/Base64.pm
3             ## Version v0.2.0
4             ## Copyright(c) 2022 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2022/04/28
7             ## Modified 2023/09/08
8             ## All rights reserved.
9             ##
10             ##
11             ## This program is free software; you can redistribute it and/or modify it
12             ## under the same terms as Perl itself.
13             ##----------------------------------------------------------------------------
14             package HTTP::Promise::Stream::Base64;
15             BEGIN
16             {
17 7     7   238838 use strict;
  7         50  
  7         369  
18 7     7   73 use warnings;
  7         36  
  7         460  
19 7     7   812 use HTTP::Promise::Stream;
  7         40  
  7         105  
20 7     7   3603 use parent -norequire, qw( HTTP::Promise::Stream::Generic );
  7         34  
  7         153  
21 7     7   636 use vars qw( @EXPORT_OK $VERSION $EXCEPTION_CLASS $Base64Error );
  7         33  
  7         777  
22 7     7   1669 use Crypt::Misc ();
  7         46906  
  7         311  
23             use constant {
24 7         1773 ENCODE_BUFFER_SIZE => 300,
25             DECODE_BUFFER_SIZE => ( 32 * 1024 ),
26 7     7   83 };
  7         37  
27 7     7   57 our @EXPORT_OK = qw( decode_b64 encode_b64 );
28 7         44 our $EXCEPTION_CLASS = 'HTTP::Promise::Exception';
29 7         188 our $VERSION = 'v0.2.0';
30             };
31              
32 7     7   62 use strict;
  7         17  
  7         195  
33 7     7   52 use warnings;
  7         40  
  7         4895  
34              
35             sub decode
36             {
37 14     14 1 2837 my $self = shift( @_ );
38 14         38 my $from = shift( @_ );
39 14         57 my $to = shift( @_ );
40 14         114 my $opts = $self->_get_args_as_hash( @_ );
41 14         296 my( $from_fh, $reader ) = $self->_get_glob_from_arg( $from );
42 14         97 my( $to_fh, $writer ) = $self->_get_glob_from_arg( $to, write => 1 );
43 14 50 33     441 return( $self->pass_error ) if( !defined( $from_fh ) || !defined( $to_fh ) );
44 14         87 my( $n, $buff );
45            
46 14         178 while( $n = $reader->( $buff, DECODE_BUFFER_SIZE ) )
47             {
48 14         302 my $decoded = Crypt::Misc::decode_b64( $buff );
49 14         114 my $rv = $writer->( $decoded );
50 14 50       137 return( $self->pass_error ) if( !defined( $rv ) );
51             }
52 14 50       91 return( $self->pass_error ) if( !defined( $n ) );
53 14         178 return( $self );
54             }
55              
56             sub decode_b64
57             {
58 13     13 1 302 my $s = __PACKAGE__->new;
59 13         213 my $rv = $s->decode( @_ );
60 13 50       5662 if( !defined( $rv ) )
61             {
62 0         0 $Base64Error = $s->error;
63 0         0 return;
64             }
65             else
66             {
67 13         67 undef( $Base64Error );
68 13         138 return( $rv );
69             }
70             }
71              
72             sub encode
73             {
74 8     8 1 9697 my $self = shift( @_ );
75 8         50 my $from = shift( @_ );
76 8         44 my $to = shift( @_ );
77 8         88 my $opts = $self->_get_args_as_hash( @_ );
78 8         406 my( $from_fh, $reader ) = $self->_get_glob_from_arg( $from );
79 8         94 my( $to_fh, $writer ) = $self->_get_glob_from_arg( $to, write => 1 );
80 8 50 33     225 return( $self->pass_error ) if( !defined( $from_fh ) || !defined( $to_fh ) );
81 8 100       114 my $eol = exists( $opts->{eol} ) ? $opts->{eol} : $/;
82 8         61 my $has_eol = length( $eol );
83 8         38 my( $n, $buff );
84            
85 8         183 while( $n = $reader->( $buff, ENCODE_BUFFER_SIZE ) )
86             {
87 308         1517 my $encoded = Crypt::Misc::encode_b64( $buff );
88 308 100       593 if( $has_eol )
89             {
90 307         3019 $encoded =~ s/(.{76})/$1$eol/g;
91             }
92 308         699 my $rv = $writer->( $encoded );
93 308 50       993 return( $self->pass_error ) if( !defined( $rv ) );
94             }
95 8 50       91 return( $self->pass_error ) if( !defined( $n ) );
96 8         136 return( $self );
97             }
98              
99             sub encode_b64
100             {
101 7     7 1 192 my $s = __PACKAGE__->new;
102 7         124 my $rv = $s->encode( @_ );
103 7 50       2447 if( !defined( $rv ) )
104             {
105 0         0 $Base64Error = $s->error;
106 0         0 return;
107             }
108             else
109             {
110 7         39 undef( $Base64Error );
111 7         88 return( $rv );
112             }
113             }
114              
115             sub is_decoder_installed
116             {
117 0     0 1   eval( 'use Crypt::Misc ();' );
118 0 0         return( $@ ? 0 : 1 );
119             }
120              
121             sub is_encoder_installed
122             {
123 0     0 1   eval( 'use Crypt::Misc ();' );
124 0 0         return( $@ ? 0 : 1 );
125             }
126              
127             # NOTE: sub FREEZE is inherited
128              
129             # NOTE: sub STORABLE_freeze is inherited
130              
131             # NOTE: sub STORABLE_thaw is inherited
132              
133             # NOTE: sub THAW is inherited
134              
135             1;
136             # NOTE: POD
137             __END__
138              
139             =encoding utf-8
140              
141             =head1 NAME
142              
143             HTTP::Promise::Stream::Base64 - Stream Encoder for Base64 Encoding
144              
145             =head1 SYNOPSIS
146              
147             use HTTP::Promise::Stream::Base64;
148             my $s = HTTP::Promise::Stream::Base64->new ||
149             die( HTTP::Promise::Stream::Base64->error, "\n" );
150             $s->encode( $input => $output, eol => "\n" ) ||
151             die( $s->error );
152             $s->decode( $input => $output ) || die( $s->error );
153             HTTP::Promise::Stream::Base64::encode_b64( $input => $output, eol => "\n" ) ||
154             die( $HTTP::Promise::Stream::Base64::Base64Error );
155             HTTP::Promise::Stream::Base64::decode_b64( $input => $output, eol => "\n" ) ||
156             die( $HTTP::Promise::Stream::Base64::Base64Error );
157              
158             =head1 VERSION
159              
160             v0.2.0
161              
162             =head1 DESCRIPTION
163              
164             This implements an encoding and decoding mechanism for base64 encoding using either of the following on input and output:
165              
166             =over 4
167              
168             =item C<filepath>
169              
170             If the parameter is neither a scalar reference nor a file handle, it will be assumed to be a file path.
171              
172             =item C<file handle>
173              
174             This can be a native file handle, or an object oriented one as long as it implements the C<print> or C<write>, and C<read> methods. The C<read> method is expected to return the number of bytes read or C<undef> upon error. The C<print> and C<write> methods are expected to simply return true upon success and C<undef> upon error.
175              
176             Alternatively, those methods can die and those exceptions wil be caught.
177              
178             =item C<scalar reference>
179              
180             This can be a simple scalar reference, or an object scalar reference.
181              
182             =back
183              
184             =head1 CONSTRUCTOR
185              
186             =head2 new
187              
188             Creates a new L<HTTP::Promise::Stream::Base64> object and returns it.
189              
190             =head1 METHODS
191              
192             =head2 decode
193              
194             This takes 2 arguments: an input and an output. Each one can be either a file path, a file handle, or a scalar reference.
195              
196             It will decode the base64 encoded data and write the result into the output.
197              
198             It returns true upon success and sets an L<error|Module::Generic/error> and return C<undef> upon error.
199              
200             =head2 encode
201              
202             This takes 2 arguments: an input and an output. Each one can be either a file path, a file handle, or a scalar reference.
203              
204             It will encode the data into base64 encoded data and write the result into the output.
205              
206             If the option I<eol> (standing for "End of line") is provided, it will be used to break down the base64 encoded into lines of 76 characters ending with the I<eol>. If I<eol> is not provided, it will default to C<$/>, which usually is C<\n>. If you want base64 data that are not borken down into 76 characters line, then pass an empty I<eol> parameter, such as:
207              
208             my $s = HTTP::Promise::Stream::Base64->new;
209             $s->encode( $from => $to, eol => undef ); # or eol => ''
210              
211             It returns true upon success and sets an L<error|Module::Generic/error> and return C<undef> upon error.
212              
213             =head1 CLASS FUNCTIONS
214              
215             The following class functions are available and can also be exported, such as:
216              
217             use HTTP::Promise::Stream::Base64 qw( decode_b64 encode_b64 );
218              
219             =head2 decode_b64
220              
221             This takes the same 2 arguments used in L</decode>: an input and an output. Each one can be either a file path, a file handle, or a scalar reference.
222              
223             It will decode the base64 encoded data and write the result into the output.
224              
225             It returns true upon success, and upon error, it will set the error in the global variable C<$Base64Error> and return C<undef>
226              
227             my $decoded = HTTP::Promise::Stream::Base64::decode_b64( $encoded );
228             die( "Something went wrong: $HTTP::Promise::Stream::Base64::Base64Error\n" if( !defined( $decoded ) );
229             print( "Decoded data is: $decoded\n" );
230              
231             =head2 encode_b64
232              
233             This takes the same 2 arguments used in L</encode>: an input and an output. Each one can be either a file path, a file handle, or a scalar reference.
234              
235             It will encode the data into base64 encoded data and write the result into the output.
236              
237             It returns true upon success, and upon error, it will set the error in the global variable C<$Base64Error> and return C<undef>
238              
239             my $encoded = HTTP::Promise::Stream::Base64::encode_b64( $data );
240             die( "Something went wrong: $HTTP::Promise::Stream::Base64::Base64Error\n" if( !defined( $encoded ) );
241             print( "Encoded data is: $encoded\n" );
242              
243             =head2 is_decoder_installed
244              
245             Returns true if the module L<Crypt::Misc> is installed, false otherwise.
246              
247             =head2 is_encoder_installed
248              
249             Returns true if the module L<Crypt::Misc> is installed, false otherwise.
250              
251             =head1 AUTHOR
252              
253             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
254              
255             =head1 SEE ALSO
256              
257             L<W3C|http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2>
258              
259             L<caniuse|https://caniuse.com/atob-btoa>
260              
261             L<PerlIO::via::Base64>
262              
263             L<HTTP::Promise>, L<HTTP::Promise::Request>, L<HTTP::Promise::Response>, L<HTTP::Promise::Message>, L<HTTP::Promise::Entity>, L<HTTP::Promise::Headers>, L<HTTP::Promise::Body>, L<HTTP::Promise::Body::Form>, L<HTTP::Promise::Body::Form::Data>, L<HTTP::Promise::Body::Form::Field>, L<HTTP::Promise::Status>, L<HTTP::Promise::MIME>, L<HTTP::Promise::Parser>, L<HTTP::Promise::IO>, L<HTTP::Promise::Stream>, L<HTTP::Promise::Exception>
264              
265             =head1 COPYRIGHT & LICENSE
266              
267             Copyright(c) 2022 DEGUEST Pte. Ltd.
268              
269             All rights reserved.
270              
271             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
272              
273             =cut