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   233715 use strict;
  7         43  
  7         359  
18 7     7   76 use warnings;
  7         35  
  7         495  
19 7     7   795 use HTTP::Promise::Stream;
  7         35  
  7         115  
20 7     7   3510 use parent -norequire, qw( HTTP::Promise::Stream::Generic );
  7         40  
  7         199  
21 7     7   678 use vars qw( @EXPORT_OK $VERSION $EXCEPTION_CLASS $Base64Error );
  7         29  
  7         796  
22 7     7   1672 use Crypt::Misc ();
  7         46353  
  7         340  
23             use constant {
24 7         1337 ENCODE_BUFFER_SIZE => 300,
25             DECODE_BUFFER_SIZE => ( 32 * 1024 ),
26 7     7   66 };
  7         32  
27 7     7   67 our @EXPORT_OK = qw( decode_b64 encode_b64 );
28 7         35 our $EXCEPTION_CLASS = 'HTTP::Promise::Exception';
29 7         202 our $VERSION = 'v0.2.0';
30             };
31              
32 7     7   80 use strict;
  7         24  
  7         179  
33 7     7   47 use warnings;
  7         27  
  7         4794  
34              
35             sub decode
36             {
37 14     14 1 2475 my $self = shift( @_ );
38 14         49 my $from = shift( @_ );
39 14         75 my $to = shift( @_ );
40 14         101 my $opts = $self->_get_args_as_hash( @_ );
41 14         205 my( $from_fh, $reader ) = $self->_get_glob_from_arg( $from );
42 14         108 my( $to_fh, $writer ) = $self->_get_glob_from_arg( $to, write => 1 );
43 14 50 33     492 return( $self->pass_error ) if( !defined( $from_fh ) || !defined( $to_fh ) );
44 14         95 my( $n, $buff );
45            
46 14         186 while( $n = $reader->( $buff, DECODE_BUFFER_SIZE ) )
47             {
48 14         254 my $decoded = Crypt::Misc::decode_b64( $buff );
49 14         91 my $rv = $writer->( $decoded );
50 14 50       127 return( $self->pass_error ) if( !defined( $rv ) );
51             }
52 14 50       92 return( $self->pass_error ) if( !defined( $n ) );
53 14         229 return( $self );
54             }
55              
56             sub decode_b64
57             {
58 13     13 1 309 my $s = __PACKAGE__->new;
59 13         233 my $rv = $s->decode( @_ );
60 13 50       5275 if( !defined( $rv ) )
61             {
62 0         0 $Base64Error = $s->error;
63 0         0 return;
64             }
65             else
66             {
67 13         64 undef( $Base64Error );
68 13         148 return( $rv );
69             }
70             }
71              
72             sub encode
73             {
74 8     8 1 9749 my $self = shift( @_ );
75 8         42 my $from = shift( @_ );
76 8         33 my $to = shift( @_ );
77 8         76 my $opts = $self->_get_args_as_hash( @_ );
78 8         355 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     197 return( $self->pass_error ) if( !defined( $from_fh ) || !defined( $to_fh ) );
81 8 100       119 my $eol = exists( $opts->{eol} ) ? $opts->{eol} : $/;
82 8         55 my $has_eol = length( $eol );
83 8         35 my( $n, $buff );
84            
85 8         158 while( $n = $reader->( $buff, ENCODE_BUFFER_SIZE ) )
86             {
87 308         1614 my $encoded = Crypt::Misc::encode_b64( $buff );
88 308 100       552 if( $has_eol )
89             {
90 307         3036 $encoded =~ s/(.{76})/$1$eol/g;
91             }
92 308         726 my $rv = $writer->( $encoded );
93 308 50       1030 return( $self->pass_error ) if( !defined( $rv ) );
94             }
95 8 50       190 return( $self->pass_error ) if( !defined( $n ) );
96 8         140 return( $self );
97             }
98              
99             sub encode_b64
100             {
101 7     7 1 201 my $s = __PACKAGE__->new;
102 7         93 my $rv = $s->encode( @_ );
103 7 50       2545 if( !defined( $rv ) )
104             {
105 0         0 $Base64Error = $s->error;
106 0         0 return;
107             }
108             else
109             {
110 7         45 undef( $Base64Error );
111 7         98 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