File Coverage

lib/OAuthomatic/Internal/Util.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


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