File Coverage

blib/lib/Reddit/Client/Request.pm
Criterion Covered Total %
statement 79 138 57.2
branch 15 48 31.2
condition 12 26 46.1
subroutine 14 16 87.5
pod 4 6 66.6
total 124 234 52.9


line stmt bran cond sub pod time code
1             package Reddit::Client::Request;
2              
3 5     5   111481 use strict;
  5         17  
  5         153  
4 5     5   29 use warnings;
  5         10  
  5         110  
5 5     5   26 use Carp;
  5         8  
  5         294  
6              
7 5     5   726 use JSON qw/encode_json decode_json/;
  5         10375  
  5         36  
8 5     5   4323 use LWP::UserAgent qw//;
  5         224351  
  5         189  
9 5     5   59 use HTTP::Request qw//;
  5         11  
  5         100  
10 5     5   2353 use URI::Encode qw/uri_encode/;
  5         80096  
  5         408  
11 5     5   46 use URI::Escape qw/uri_escape/; # next update, also line 122
  5         17  
  5         259  
12 5     5   29 use Data::Dumper;
  5         13  
  5         389  
13              
14             require Reddit::Client;
15              
16             use fields (
17 5         57 'user_agent',
18             'method',
19             'url',
20             'query',
21             'post_data',
22             'cookie',
23             'modhash',
24             'token',
25             'tokentype',
26             'request_errors',
27             'print_response',
28             'print_request',
29             'print_request_on_error',
30             'last_token',
31 5     5   478 );
  5         1682  
32              
33             sub new {
34 3     3 1 3585 my ($class, %param) = @_;
35 3         13 my $self = fields::new($class);
36 3   66     4205 $self->{user_agent} = $param{user_agent} || croak 'Expected "user_agent"';
37 2   66     96 $self->{url} = $param{url} || croak 'Expected "url"';
38 1         3 $self->{query} = $param{query};
39 1         3 $self->{post_data} = $param{post_data};
40 1         2 $self->{cookie} = $param{cookie};
41 1         3 $self->{modhash} = $param{modhash};
42 1         3 $self->{token} = $param{token};
43 1         3 $self->{tokentype} = $param{tokentype};
44 1   50     9 $self->{request_errors} = $param{request_errors} || 0;
45 1   50     6 $self->{print_response} = $param{print_response} || 0;
46 1   50     5 $self->{print_request} = $param{print_request} || 0;
47 1   50     6 $self->{print_request_on_error} = $param{print_request_on_error} || 0;
48 1         3 $self->{last_token} = $param{last_token};
49              
50 1 50       5 if (defined $self->{query}) {
51 1 50       6 ref $self->{query} eq 'HASH' || croak 'Expected HASH ref for "query"';
52 1         5 $self->{url} = sprintf('%s?%s', $self->{url}, build_query($self->{query}))
53             }
54              
55 1 50       2713 if (defined $self->{post_data}) {
56 1 50       5 ref $self->{post_data} eq 'HASH' || croak 'Expected HASH ref for "post_data"';
57             }
58              
59 1   50     4 $self->{method} = $param{method} || 'GET';
60 1         6 $self->{method} = uc $self->{method};
61              
62 1         5 return $self;
63             }
64              
65             sub build_query {
66 8 100   8 1 2617 my $param = shift or return '';
67 7         19 my $opt = { encode_reserved => 1 };
68 7         41 join '&', map {uri_encode($_, $opt) . '=' . uri_encode($param->{$_}, $opt)} sort keys %$param;
  13         16649  
69             }
70              
71             sub build_request {
72 3     3 1 302 my $self = shift;
73 3         18 my $request = HTTP::Request->new();
74              
75 3         189 $request->uri($self->{url});
76 3 0 33     7484 $request->header("Authorization"=> "$self->{tokentype} $self->{token}") if $self->{tokentype} && $self->{token};
77              
78 3 50       11 if ($self->{method} eq 'POST') {
    0          
    0          
79 3   50     10 my $post_data = $self->{post_data} || {};
80 3 50       11 $post_data->{modhash} = $self->{modhash} if $self->{modhash};
81 3 50       8 $post_data->{uh} = $self->{modhash} if $self->{modhash};
82              
83 3         11 $request->method('POST');
84 3         47 $request->content_type('application/x-www-form-urlencoded');
85 3         137 $request->content(build_query($post_data));
86             } elsif ($self->{method} eq 'DELETE') {
87 0         0 $request->method('DELETE');
88             } elsif ($self->{method} eq 'PUT') {
89 0   0     0 my $post_data = $self->{post_data} || {};
90 0 0       0 $post_data->{modhash} = $self->{modhash} if $self->{modhash};
91 0 0       0 $post_data->{uh} = $self->{modhash} if $self->{modhash};
92              
93 0         0 $request->method('PUT');
94 0         0 $request->content_type('application/x-www-form-urlencoded');
95 0         0 $request->content(build_query($post_data));
96             } else {
97 0         0 $request->method('GET');
98             }
99              
100 3         6976 return $request;
101             }
102              
103             sub send {
104 2     2 1 2073 my $self = shift;
105 2         8 my $request = $self->build_request;
106              
107 2         16 Reddit::Client::DEBUG('%4s request to %s', $self->{method}, $self->{url});
108              
109 2         17 my $ua = LWP::UserAgent->new(agent => $self->{user_agent}, env_proxy => 1);
110 2         7756 my $res = $ua->request($request);
111              
112             #print Dumper($res); # slightly more redable because more compact
113 2 50       198 if ($self->{print_request}) {
    50          
114 0         0 print Dumper($request);
115 0         0 print Dumper($res);
116             } elsif ($self->{print_response}) {
117 0         0 print $res->content . "\n";
118             }
119              
120             # response is an HTTP::Response object, sent is HTTP::Request
121              
122 2 100       7 if ($res->is_success) {
123 1         12 return $res->content;
124             } else {
125             # I don't know what the fuck any of this is
126             # print request unless we already printed it
127 1 50 33     17 if ($self->{print_request_on_error} and !$self->{print_request}) {
    50          
128 0         0 print Dumper($request);
129 0         0 print Dumper($res);
130             } elsif ($self->{request_errors}) {
131 0         0 my $json;
132 0         0 my $success = eval { $json = decode_json $res->{_content}; };
  0         0  
133              
134             # If Reddit returned valid json, add it to a hash and print it
135 0 0       0 if ($success) {
136 0         0 my $err = {
137             error => 1,
138             code => $res->code,
139             status_line => $res->status_line,
140             data => $json,
141             };
142            
143 0         0 my $rtn = encode_json $err;
144 0         0 die "$rtn\n";
145              
146             } else {
147 0         0 die "Request error: HTTP ".$res->status_line .", Content: $res->{_content}";
148             }
149             #die $res->{_content}."\n";
150             } else { # default: print status line and exit
151             #croak sprintf("Request error: HTTP %s last token: %s time: %s", $res->status_line, $self->{last_token}, time);
152 1         5 die sprintf("Request error: HTTP %s\n", $res->status_line);
153             }
154             }
155             }
156              
157             sub token_request {
158 0     0 0   my ($self, %param) = @_;
159              
160 0           my $url = "https://$param{client_id}:$param{secret}\@www.reddit.com/api/v1/access_token";
161              
162 0           my $ua = LWP::UserAgent->new(agent => $param{user_agent});
163 0           my $req = HTTP::Request->new(POST => $url);
164 0           $req->header('content-type' => 'application/x-www-form-urlencoded');
165              
166             #my $postdata = "grant_type=password&username=$username&password=$password";
167 0           my $postdata;
168            
169 0 0         if ($param{auth_type} eq 'script') {
    0          
170 0           $postdata = "grant_type=password&username=$param{username}&password=" . uri_escape($param{password});
171             } elsif ($param{auth_type} eq 'webapp') {
172 0           $postdata = "grant_type=refresh_token&refresh_token=".uri_escape($param{refresh_token});
173 0           } else { die "Request:token_request: invalid auth type"; }
174              
175 0           $req->content($postdata);
176              
177 0           my $res = $ua->request($req);
178              
179 0 0         if ($res->is_success) {
180 0           return $res->decoded_content;
181             } else {
182             # this is sometimes called in static context
183             #if ($self->{request_errors}) {
184             # croak "Request error: HTTP ".$res->status_line .", Content: $res->{_content}";
185             #} else {
186 0           croak sprintf("Request error: HTTP %s", $res->status_line);
187             #}
188             #croak sprintf('Request error: HTTP %s', $res->status_line);
189             }
190             }
191              
192             sub refresh_token_request {
193 0     0 0   my ($self, %data) = @_;
194              
195             # create user agent
196 0           my $ua = new LWP::UserAgent( agent=> $data{ua} );
197             # create new request
198 0           my $request = new HTTP::Request();
199             # set the request method
200 0           $request->method("POST");
201             # set request url
202 0           my $url = "https://$data{client_id}:$data{secret}\@www.reddit.com/api/v1/access_token";
203 0           $request->uri($url);
204              
205             my $reqdata = {
206             grant_type => 'authorization_code',
207             code => $data{code},
208             redirect_uri=> $data{redirect_uri},
209 0           duration => 'permanent',
210             };
211              
212 0           $request->content_type('application/x-www-form-urlencoded');
213              
214 0           my $opt = { encode_reserved => 1 };
215 0           my $encoded = join '&', map { uri_encode($_, $opt) . '=' . uri_encode($reqdata->{$_}, $opt) } sort keys %$reqdata;
  0            
216              
217 0           $request->content($encoded);
218              
219 0           my $result = $ua->request($request);
220              
221 0 0         if ($data{print_request}) {
222 0           print Dumper($request);
223 0           print Dumper($result);
224             }
225              
226 0 0         if ($result->is_success) {
227 0           my $j = decode_json $result->content;
228 0           my $tok = $j->{refresh_token};
229              
230 0           return $tok;
231             } else {
232 0           print "Request error: HTTP ".$result->status_line.", Content:\n$result->{_content}\n";
233 0           print "refresh_token_request: something went wrong. To aid in debugging, you can set 'print_request' to true when creating a new Reddit::Client object. This will print the entire content of the request and response.\n";
234              
235 0           die;
236             }
237              
238             }
239              
240             1;
241              
242             __END__