File Coverage

lib/OAuthomatic/Internal/Util.pm
Criterion Covered Total %
statement 31 33 93.9
branch n/a
condition n/a
subroutine 11 11 100.0
pod n/a
total 42 44 95.4


line stmt bran cond sub pod time code
1             package OAuthomatic::Internal::Util;
2             # ABSTRACT: internal helper routines (form parsing and filling)
3              
4              
5 3     3   2323 use strict;
  3         6  
  3         119  
6 3     3   12 use warnings;
  3         5  
  3         99  
7 3     3   13 use base 'Exporter';
  3         3  
  3         325  
8             our @EXPORT = qw(
9             fill_httpmsg_form parse_httpmsg_form
10             fill_httpmsg_text
11             serialize_json fill_httpmsg_json parse_httpmsg_json
12             );
13              
14 3     3   1419 use HTTP::Request;
  3         50476  
  3         101  
15 3     3   1484 use HTTP::Response;
  3         13737  
  3         82  
16 3     3   1449 use HTTP::Body;
  3         71265  
  3         110  
17 3     3   21 use URI;
  3         3  
  3         59  
18 3     3   1191 use URI::QueryParam;
  3         1549  
  3         77  
19 3     3   493 use Encode;
  3         7036  
  3         229  
20 3     3   464 use utf8;
  3         11  
  3         18  
21 3     3   633 use JSON qw/decode_json encode_json from_json to_json/;
  0            
  0            
22             use Try::Tiny;
23             use Scalar::Util qw(reftype);
24             use namespace::sweep;
25              
26             # FIXME: throw on errors
27              
28              
29             sub fill_httpmsg_form {
30             my ($http_message, $params) = @_;
31              
32             my $body_form = URI->new('http:');
33             $body_form->query_form_hash($params);
34             $http_message->content($body_form->query());
35             $http_message->content_type("application/x-www-form-urlencoded; charset=utf-8");
36             }
37              
38              
39             sub parse_httpmsg_form {
40             my ($http_message, $force_form) = @_;
41              
42             my ($content_type, $encoding) = $http_message->content_type;
43             my $charset;
44              
45             if($content_type =~ m{^text/}) {
46             if($force_form) {
47             $content_type = 'application/x-www-form-urlencoded';
48             }
49             }
50              
51             if($encoding) {
52             if($encoding =~ /^charset=(.*)/i) {
53             $charset = lc($1);
54             }
55             }
56              
57             my $body = HTTP::Body->new(
58             $content_type,
59             $http_message->content_length);
60             $body->add($http_message->content);
61             my $params = $body->param;
62              
63             # HTTP::Body does not decode
64             if($charset) {
65             if($charset =~ /^utf-?8$/) {
66             for my $value (values %$params) {
67             unless ( ref $value && ref $value ne 'ARRAY' ) {
68             utf8::decode($_) for ( ref($value) ? @{$value} : $value );
69             }
70             }
71             } else {
72             foreach my $key (keys %$params) {
73             my $value = $params->{$key};
74             unless( ref($value) ) {
75             $params->{$key} = decode($charset, $value);
76             } elsif( ref($value) eq 'ARRAY') {
77             my @fixed = map { decode($charset, $_) } @$value;
78             $params->{$key} = \@fixed;
79             }
80             }
81             }
82             }
83              
84             return $params;
85              
86             # For comparison: this usually works OK too (albeit is too magic for my taste)
87             # use CGI qw();
88             # my %vars = CGI->new($http_message->content)->Vars;
89             # return \%vars;
90             }
91              
92              
93             sub fill_httpmsg_text {
94             my ($http_message, $text, $content_type) = @_;
95              
96             my $text_ref = ref($text) ? $text : \$text;
97              
98             if(utf8::is_utf8($$text_ref)) {
99             my $encoding;
100             my $using_utf8;
101             if( $content_type =~ /\bcharset=([; ]+)/x) {
102             $encoding = $1 || 'ascii';
103             if($encoding =~ /^utf-?8$/xi) {
104             $using_utf8 = 1;
105             }
106             }
107             # For utf-8 we may leave thing as is, octets are OK. Otherwise...
108             unless($using_utf8) {
109             $text = encode($encoding, $$text_ref, Encode::FB_WARN); # FIXME: maybe throw...
110             $text_ref = \$text;
111             }
112             }
113              
114             $http_message->content($$text_ref);
115             $http_message->content_type($content_type);
116             }
117              
118              
119              
120             sub serialize_json {
121             my $json = shift;
122              
123             if(reftype($json) =~ /^(?:HASH|ARRAY)$/) {
124             return encode_json($json); # FIXME rethrow exception as sth better
125             }
126             elsif(! ref($json) || reftype($json) eq 'SCALAR') {
127             return $json;
128             }
129             else {
130             OAuthomatic::Error::Generic->throw(
131             ident => "Can not serialize to JSON",
132             extra => "Provided type is neither hash/array ref, nor already serialized string");
133             }
134             }
135              
136              
137             sub fill_httpmsg_json {
138             my ($http_message, $json) = @_;
139              
140             fill_httpmsg_text($http_message, serialize_json($json), "application/json; charset=utf-8");
141             }
142              
143              
144             sub parse_httpmsg_json {
145             my ($http_message, $force) = @_;
146              
147             my ($content_type, $encoding) = $http_message->content_type;
148             my $charset;
149              
150             unless( $force || $content_type =~ m{^(application/(?:x-)?json|text/plain)$}x ) {
151             return;
152             }
153              
154             # FIXME: throw sensible exceptions on errors (preserve object...)
155             return from_json($http_message->decoded_content);
156              
157             # if($encoding) {
158             # if($encoding =~ /^charset=(.*)/i) {
159             # $charset = lc($1);
160             # }
161             # }
162             # my $json;
163             # if($r->content_encoding) {
164             # $json = from_json(decode($r->content_encoding, $r->content));
165             # } else {
166             # $json = decode_json($r->content);
167             # }
168             # return $json;
169              
170             }
171              
172              
173             1;
174              
175             __END__
176              
177             =pod
178              
179             =encoding UTF-8
180              
181             =head1 NAME
182              
183             OAuthomatic::Internal::Util - internal helper routines (form parsing and filling)
184              
185             =head1 VERSION
186              
187             version 0.01
188              
189             =head1 DESCRIPTION
190              
191             Internally used by L<OAuthomatic>
192              
193             =head1 EXPORTS FUNCTIONS
194              
195             =head2 fill_httpmsg_form($http_message, $params)
196              
197             Serializes $params (dict ref) as form data and sets $http_message (HTTP::Request or HTTP::Response)
198             content with that data.
199              
200             =head2 parse_httpmsg_form($http_message, $:force_form)
201              
202             Parses content as message, returns hashref (empty if parsing failed,
203             content type is not parseable etc). Supports a few content types (as
204             HTTP::Body).
205              
206             With $force_form parses also things with incorrect content type.
207              
208             =head2 fill_httpmsg_text($http_message, $text, $content_type)
209              
210             Fills given HTTP::Message content with given text, using encoding
211             specified inside content type to serialize if text is provided as perl
212             unicode string (and appending text as is if it is binary string).
213              
214             Set's also content_type.
215              
216             $text can also be specified as reference to string.
217              
218             =head2 serialize_json($json)
219              
220             Serializes JSON to utf-8 encoded string. If $json is already string or string-ref, leaves it as is.
221              
222             Function defined to keep conventions in one place.
223              
224             =head2 fill_httpmsg_json($http_message, $json)
225              
226             Serializes $params (dict ref) as json data and sets $http_message
227             (HTTP::Request or HTTP::Response) content with that data.
228              
229             In case $json is already scalar or scalar ref, passes it on assuming
230             it is already serialized.
231              
232             =head2 parse_httpmsg_json($http_message, $:force)
233              
234             Parses content as message, returns hashref (empty if parsing failed,
235             content type is not parseable etc).
236              
237             With $force parses also things with incorrect content type.
238              
239             =head1 AUTHOR
240              
241             Marcin Kasperski <Marcin.Kasperski@mekk.waw.pl>
242              
243             =head1 COPYRIGHT AND LICENSE
244              
245             This software is copyright (c) 2015 by Marcin Kasperski.
246              
247             This is free software; you can redistribute it and/or modify it under
248             the same terms as the Perl 5 programming language system itself.
249              
250             =cut