line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
##---------------------------------------------------------------------------- |
2
|
|
|
|
|
|
|
## Asynchronous HTTP Request and Promise - ~/lib/HTTP/Promise/Response.pm |
3
|
|
|
|
|
|
|
## Version v0.1.0 |
4
|
|
|
|
|
|
|
## Copyright(c) 2022 DEGUEST Pte. Ltd. |
5
|
|
|
|
|
|
|
## Author: Jacques Deguest <jack@deguest.jp> |
6
|
|
|
|
|
|
|
## Created 2022/03/21 |
7
|
|
|
|
|
|
|
## Modified 2022/03/21 |
8
|
|
|
|
|
|
|
## All rights reserved |
9
|
|
|
|
|
|
|
## |
10
|
|
|
|
|
|
|
## This program is free software; you can redistribute it and/or modify it |
11
|
|
|
|
|
|
|
## under the same terms as Perl itself. |
12
|
|
|
|
|
|
|
##---------------------------------------------------------------------------- |
13
|
|
|
|
|
|
|
package HTTP::Promise::Response; |
14
|
|
|
|
|
|
|
BEGIN |
15
|
|
|
|
|
|
|
{ |
16
|
4
|
|
|
4
|
|
255825
|
use strict; |
|
4
|
|
|
|
|
22
|
|
|
4
|
|
|
|
|
170
|
|
17
|
4
|
|
|
4
|
|
32
|
use warnings; |
|
4
|
|
|
|
|
13
|
|
|
4
|
|
|
|
|
168
|
|
18
|
4
|
|
|
4
|
|
27
|
use warnings::register; |
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
900
|
|
19
|
4
|
|
|
4
|
|
34
|
use parent qw( HTTP::Promise::Message ); |
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
62
|
|
20
|
4
|
|
|
4
|
|
374
|
use vars qw( $DEFAULT_PROTOCOL $VERSION ); |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
329
|
|
21
|
4
|
|
|
4
|
|
3030
|
use HTTP::Promise::Status; |
|
4
|
|
|
|
|
17
|
|
|
4
|
|
|
|
|
19
|
|
22
|
4
|
|
|
4
|
|
24
|
our $DEFAULT_PROTOCOL = 'HTTP/1.0'; |
23
|
4
|
|
|
|
|
79
|
our $VERSION = 'v0.1.0'; |
24
|
|
|
|
|
|
|
}; |
25
|
|
|
|
|
|
|
|
26
|
4
|
|
|
4
|
|
28
|
use strict; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
95
|
|
27
|
4
|
|
|
4
|
|
30
|
use warnings; |
|
4
|
|
|
|
|
24
|
|
|
4
|
|
|
|
|
9041
|
|
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
sub init |
30
|
|
|
|
|
|
|
{ |
31
|
8
|
|
|
8
|
1
|
1724
|
my $self = shift( @_ ); |
32
|
8
|
|
|
|
|
34
|
my( $code, $status ); |
33
|
8
|
100
|
|
|
|
45
|
if( @_ ) |
34
|
|
|
|
|
|
|
{ |
35
|
5
|
50
|
33
|
|
|
206
|
if( @_ == 1 && ref( $_[0] ) eq 'HASH' ) |
|
|
50
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
36
|
|
|
|
|
|
|
{ |
37
|
0
|
|
|
|
|
0
|
my $opts = $_[0]; |
38
|
0
|
|
|
|
|
0
|
( $code, $status ) = CORE::delete( @$opts{qw( code status )} ); |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
elsif( @_ >= 2 && |
41
|
|
|
|
|
|
|
defined( $_[0] ) && |
42
|
|
|
|
|
|
|
$_[0] =~ /^\d{3}$/ && |
43
|
|
|
|
|
|
|
( ( defined( $_[1] ) && $_[1] =~ /\S/ ) || !defined( $_[1] ) ) ) |
44
|
|
|
|
|
|
|
{ |
45
|
5
|
|
|
|
|
31
|
( $code, $status ) = splice( @_, 0, 2 ); |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
else |
48
|
|
|
|
|
|
|
{ |
49
|
0
|
0
|
|
|
|
0
|
return( $self->error( "Invalid parameters received. I was expecting either an hash reference or at least a code and status, but instead got: ", join( ", ", map( defined( $_ ) ? "'$_'" : 'undef', @_ ) ) ) ); |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
# properties headers and content are set by our parent class HTTP::Promise::Message |
53
|
8
|
|
|
|
|
272
|
$self->{code} = $code; |
54
|
8
|
|
|
|
|
36
|
$self->{cookies} = []; |
55
|
8
|
|
|
|
|
39
|
$self->{default_protocol} = $DEFAULT_PROTOCOL; |
56
|
8
|
|
|
|
|
38
|
$self->{previous} = undef; |
57
|
8
|
|
|
|
|
30
|
$self->{request} = undef; |
58
|
8
|
|
|
|
|
29
|
$self->{status} = $status; |
59
|
8
|
|
|
|
|
37
|
$self->{version} = ''; |
60
|
8
|
|
|
|
|
28
|
$self->{_init_strict_use_sub} = 1; |
61
|
8
|
|
|
|
|
45
|
$self->{_init_params_order} = [qw( content headers )]; |
62
|
|
|
|
|
|
|
# $self->SUPER::init( ( defined( $headers ) ? $headers : () ), @_ ) || return( $self->pass_error ); |
63
|
8
|
50
|
|
|
|
82
|
$self->SUPER::init( @_ ) || return( $self->pass_error ); |
64
|
8
|
|
|
|
|
176
|
return( $self ); |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub base |
68
|
|
|
|
|
|
|
{ |
69
|
4
|
|
|
4
|
1
|
554
|
my $self = shift( @_ ); |
70
|
4
|
|
|
|
|
18
|
my $base = ( |
71
|
|
|
|
|
|
|
$self->header( 'Content-Base' ), # used to be HTTP/1.1 |
72
|
|
|
|
|
|
|
$self->header( 'Content-Location' ), # HTTP/1.1 |
73
|
|
|
|
|
|
|
$self->header( 'Base' ), # HTTP/1.0 |
74
|
|
|
|
|
|
|
)[0]; |
75
|
4
|
50
|
|
|
|
26
|
$self->_load_class( 'URI', { version => 5.10 } ) || return( $self->pass_error ); |
76
|
4
|
50
|
66
|
|
|
164
|
if( $base && $base =~ /^[a-zA-Z][a-zA-Z0-9.+\-]*:/ ) |
77
|
|
|
|
|
|
|
{ |
78
|
|
|
|
|
|
|
# already absolute |
79
|
0
|
|
|
|
|
0
|
return( URI->new( $base ) ); |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
4
|
|
|
|
|
11
|
my $req = $self->request; |
83
|
4
|
50
|
|
|
|
89
|
if( $req ) |
84
|
|
|
|
|
|
|
{ |
85
|
|
|
|
|
|
|
# if $base is undef here, the return value is effectively |
86
|
|
|
|
|
|
|
# just a copy of $self->request->uri. |
87
|
4
|
|
|
|
|
12
|
return( URI->new_abs( $base, $req->uri ) ); |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
# Cannot find an absolute base |
90
|
0
|
|
|
|
|
0
|
return; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
sub clone |
94
|
|
|
|
|
|
|
{ |
95
|
2
|
|
|
2
|
1
|
36006
|
my $self = shift( @_ ); |
96
|
2
|
|
|
|
|
47
|
my $new = $self->SUPER::clone; |
97
|
2
|
|
|
|
|
28
|
my $code = $self->code; |
98
|
2
|
|
|
|
|
1541
|
my $status = $self->status; |
99
|
2
|
|
|
|
|
1575
|
my $prev = $self->previous; |
100
|
2
|
|
|
|
|
54
|
my $req = $self->request; |
101
|
2
|
|
|
|
|
50
|
$new->code( $code ); |
102
|
2
|
|
|
|
|
73542
|
$new->status( $status ); |
103
|
2
|
|
|
|
|
1771
|
$new->previous( undef ); |
104
|
|
|
|
|
|
|
# $new->previous( $prev ) if( defined( $prev ) ); |
105
|
2
|
100
|
|
|
|
74
|
$new->request( $req->clone ) if( defined( $req ) ); |
106
|
2
|
|
|
|
|
50
|
return( $new ); |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
23
|
|
|
23
|
1
|
41406
|
sub code { return( shift->_set_get_number( 'code', @_ ) ); } |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
sub current_age |
112
|
|
|
|
|
|
|
{ |
113
|
13
|
|
|
13
|
1
|
168
|
my $self = shift( @_ ); |
114
|
13
|
|
|
|
|
19
|
my $time = shift( @_ ); |
115
|
|
|
|
|
|
|
|
116
|
13
|
|
|
|
|
249
|
my $h_client_date = $self->client_date; |
117
|
13
|
|
|
|
|
236
|
my $h_date = $self->date; |
118
|
|
|
|
|
|
|
# Implementation of RFC 2616 section 13.2.3 |
119
|
|
|
|
|
|
|
# (age calculations) |
120
|
13
|
100
|
|
|
|
411
|
my $response_time = $h_client_date->epoch if( $h_client_date ); |
121
|
13
|
100
|
|
|
|
429
|
my $date = $h_date->epoch if( $h_date ); |
122
|
|
|
|
|
|
|
|
123
|
13
|
|
|
|
|
301
|
my $age = 0; |
124
|
13
|
100
|
100
|
|
|
64
|
if( $response_time && $date ) |
125
|
|
|
|
|
|
|
{ |
126
|
|
|
|
|
|
|
# apparent_age |
127
|
9
|
|
|
|
|
15
|
$age = $response_time - $date; |
128
|
9
|
100
|
|
|
|
38
|
$age = 0 if( $age < 0 ); |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
|
131
|
13
|
|
|
|
|
41
|
my $age_v = $self->header( 'Age' ); |
132
|
13
|
100
|
100
|
|
|
70
|
if( $age_v && $age_v > $age ) |
133
|
|
|
|
|
|
|
{ |
134
|
|
|
|
|
|
|
# corrected_received_age |
135
|
4
|
|
|
|
|
9
|
$age = $age_v; |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
13
|
100
|
|
|
|
36
|
if( $response_time ) |
139
|
|
|
|
|
|
|
{ |
140
|
10
|
|
|
|
|
24
|
my $request = $self->request; |
141
|
10
|
100
|
|
|
|
270
|
if( $request ) |
142
|
|
|
|
|
|
|
{ |
143
|
8
|
|
|
|
|
162
|
my $req_date = $request->date; |
144
|
8
|
100
|
|
|
|
305
|
my $request_time = $req_date->epoch if( $req_date ); |
145
|
8
|
100
|
100
|
|
|
340
|
if( $request_time && $request_time < $response_time ) |
146
|
|
|
|
|
|
|
{ |
147
|
|
|
|
|
|
|
# Add response_delay to age to get 'corrected_initial_age' |
148
|
6
|
|
|
|
|
17
|
$age += $response_time - $request_time; |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
} |
151
|
10
|
|
66
|
|
|
32
|
$age += ( $time || time ) - $response_time; |
152
|
|
|
|
|
|
|
} |
153
|
13
|
|
|
|
|
97
|
return( $age ); |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
0
|
|
|
0
|
1
|
0
|
sub default_protocol { return( shift->_set_get_scalar_as_object( 'default_protocol', @_ ) ); } |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub dump |
159
|
|
|
|
|
|
|
{ |
160
|
1
|
|
|
1
|
1
|
665
|
my $self = shift( @_ ); |
161
|
1
|
|
|
|
|
6
|
my $status_line = $self->status_line; |
162
|
1
|
|
|
|
|
6
|
my $proto = $self->protocol; |
163
|
1
|
50
|
|
|
|
773
|
$status_line = "$proto $status_line" if( $proto ); |
164
|
1
|
|
|
|
|
37
|
return( $self->SUPER::dump( preheader => $status_line, @_ ) ); |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
sub filename |
168
|
|
|
|
|
|
|
{ |
169
|
10
|
|
|
10
|
1
|
4639
|
my $self = shift( @_ ); |
170
|
10
|
|
|
|
|
15
|
my $file; |
171
|
10
|
|
|
|
|
39
|
my $dispo = $self->header( 'Content-Disposition' ); |
172
|
|
|
|
|
|
|
# e.g.: attachment; filename="filename.jpg" |
173
|
|
|
|
|
|
|
# form-data; name="fieldName"; filename="filename.jpg" |
174
|
|
|
|
|
|
|
# Ref: <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition> |
175
|
10
|
100
|
|
|
|
27
|
if( $dispo ) |
176
|
|
|
|
|
|
|
{ |
177
|
7
|
|
|
|
|
23
|
my $cd = $self->headers->new_field( 'Content-Disposition' => "$dispo" ); |
178
|
7
|
50
|
|
|
|
17
|
return( $self->pass_error( $self->headers->error ) ) if( !defined( $cd ) ); |
179
|
7
|
|
|
|
|
18
|
$file = $cd->filename; |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
10
|
100
|
66
|
|
|
185
|
unless( defined( $file ) && length( $file ) ) |
183
|
|
|
|
|
|
|
{ |
184
|
5
|
50
|
|
|
|
29
|
$self->_load_class( 'URI', { version => 5.10 } ) || return( $self->pass_error ); |
185
|
5
|
|
|
|
|
188
|
my $uri; |
186
|
5
|
100
|
|
|
|
20
|
if( my $cl = $self->header( 'Content-Location' ) ) |
|
|
100
|
|
|
|
|
|
187
|
|
|
|
|
|
|
{ |
188
|
1
|
|
|
|
|
10
|
$uri = URI->new( $cl ); |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
elsif( my $request = $self->request ) |
191
|
|
|
|
|
|
|
{ |
192
|
3
|
|
|
|
|
107
|
$uri = $request->uri; |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
5
|
100
|
|
|
|
2620
|
if( $uri ) |
196
|
|
|
|
|
|
|
{ |
197
|
2
|
|
|
|
|
27
|
my $f = $self->new_file( $uri->path ); |
198
|
2
|
|
|
|
|
258668
|
$file = $f->basename; |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
10
|
100
|
|
|
|
7238
|
if( $file ) |
203
|
|
|
|
|
|
|
{ |
204
|
|
|
|
|
|
|
# basename |
205
|
7
|
|
|
|
|
78
|
$file =~ s,.*[\\/],,; |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
10
|
50
|
66
|
|
|
91
|
if( $file && !length( $file ) ) |
209
|
|
|
|
|
|
|
{ |
210
|
0
|
|
|
|
|
0
|
$file = undef; |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
10
|
|
|
|
|
236
|
return( $file ); |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
sub fresh_until |
217
|
|
|
|
|
|
|
{ |
218
|
5
|
|
|
5
|
1
|
838
|
my $self = shift( @_ ); |
219
|
5
|
|
|
|
|
31
|
my $opts = $self->_get_args_as_hash( @_ ); |
220
|
5
|
|
66
|
|
|
466
|
$opts->{time} ||= time; |
221
|
5
|
|
|
|
|
22
|
my $f = $self->freshness_lifetime( %$opts ); |
222
|
5
|
100
|
|
|
|
35
|
return unless( defined( $f ) ); |
223
|
4
|
|
|
|
|
33
|
return( $f - $self->current_age( $opts->{time} ) + $opts->{time} ); |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
sub freshness_lifetime |
227
|
|
|
|
|
|
|
{ |
228
|
24
|
|
|
24
|
1
|
1749
|
my $self = shift( @_ ); |
229
|
24
|
|
|
|
|
80
|
my $opts = $self->_get_args_as_hash( @_ ); |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
# First look for the Cache-Control: max-age=n header |
232
|
24
|
|
|
|
|
2421
|
for my $cc ( $self->header( 'Cache-Control' ) ) |
233
|
|
|
|
|
|
|
{ |
234
|
6
|
50
|
33
|
|
|
26
|
next if( !defined( $cc ) || !length( "$cc" ) ); |
235
|
6
|
|
|
|
|
17
|
for my $cc_dir ( split( /[[:blank:]\h]*,[[:blank:]\h]*/, $cc ) ) |
236
|
|
|
|
|
|
|
{ |
237
|
6
|
100
|
|
|
|
35
|
return( $1 ) if( $cc_dir =~ /^max-age[[:blank:]\h]*=[[:blank:]\h]*(\d+)/i ); |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
|
241
|
21
|
|
|
|
|
459
|
my $h_date = $self->date; |
242
|
21
|
|
|
|
|
346
|
my $h_client_date = $self->client_date; |
243
|
|
|
|
|
|
|
# Next possibility is to look at the "Expires" header |
244
|
|
|
|
|
|
|
my $date = ( $h_date ? $h_date->epoch : '' ) || |
245
|
|
|
|
|
|
|
( $h_client_date ? $h_client_date->epoch : '' ) || |
246
|
21
|
|
66
|
|
|
491
|
$opts->{time} || time; |
247
|
21
|
100
|
|
|
|
783
|
if( my $expires = $self->expires ) |
248
|
|
|
|
|
|
|
{ |
249
|
1
|
|
|
|
|
17
|
return( $expires->epoch - $date ); |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
# Must apply heuristic expiration |
253
|
20
|
100
|
100
|
|
|
113
|
return if( exists( $opts->{heuristic_expiry} ) && !$opts->{heuristic_expiry} ); |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
# Default heuristic expiration parameters |
256
|
16
|
|
100
|
|
|
86
|
$opts->{h_min} ||= 60; |
257
|
16
|
|
100
|
|
|
60
|
$opts->{h_max} ||= 24 * 3600; |
258
|
|
|
|
|
|
|
# 10% since last-mod suggested by RFC2616 |
259
|
16
|
|
100
|
|
|
76
|
$opts->{h_lastmod_fraction} ||= 0.10; |
260
|
16
|
|
100
|
|
|
65
|
$opts->{h_default} ||= 3600; |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
# Should give a warning if more than 24 hours according to |
263
|
|
|
|
|
|
|
# RFC 2616 section 13.2.4. Here we just make this the default |
264
|
|
|
|
|
|
|
# maximum value. |
265
|
16
|
100
|
|
|
|
241
|
if( my $last_modified = $self->last_modified ) |
266
|
|
|
|
|
|
|
{ |
267
|
12
|
|
|
|
|
116
|
my $h_exp = ( $date - $last_modified ) * $opts->{h_lastmod_fraction}; |
268
|
12
|
100
|
|
|
|
45619
|
return( $opts->{h_min} ) if( $h_exp < $opts->{h_min} ); |
269
|
10
|
100
|
|
|
|
108
|
return( $opts->{h_max} ) if( $h_exp > $opts->{h_max} ); |
270
|
2
|
|
|
|
|
24
|
return( $h_exp ); |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
# default when all else fails |
274
|
4
|
100
|
|
|
|
25
|
return( $opts->{h_min} ) if( $opts->{h_min} > $opts->{h_default} ); |
275
|
3
|
|
|
|
|
39
|
return( $opts->{h_default} ); |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
|
278
|
1
|
|
|
1
|
1
|
3020
|
sub is_client_error { return( HTTP::Promise::Status->is_client_error( shift->code ) ); } |
279
|
|
|
|
|
|
|
|
280
|
1
|
|
|
1
|
1
|
3024
|
sub is_error { return( HTTP::Promise::Status->is_error( shift->code ) ); } |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
sub is_fresh |
283
|
|
|
|
|
|
|
{ |
284
|
3
|
|
|
3
|
1
|
325
|
my $self = shift( @_ ); |
285
|
3
|
|
|
|
|
12
|
my $opts = $self->_get_args_as_hash( @_ ); |
286
|
3
|
|
66
|
|
|
299
|
$opts->{time} ||= time; |
287
|
3
|
|
|
|
|
13
|
my $f = $self->freshness_lifetime( %$opts ); |
288
|
3
|
100
|
|
|
|
21
|
return unless( defined( $f ) ); |
289
|
2
|
|
|
|
|
10
|
return( $f > $self->current_age( $opts->{time} ) ); |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
|
292
|
1
|
|
|
1
|
1
|
3165
|
sub is_info { return( HTTP::Promise::Status->is_info( shift->code ) ); } |
293
|
|
|
|
|
|
|
|
294
|
1
|
|
|
1
|
1
|
2970
|
sub is_redirect { return( HTTP::Promise::Status->is_redirect( shift->code ) ); } |
295
|
|
|
|
|
|
|
|
296
|
1
|
|
|
1
|
1
|
3047
|
sub is_server_error { return( HTTP::Promise::Status->is_server_error( shift->code ) ); } |
297
|
|
|
|
|
|
|
|
298
|
4
|
|
|
4
|
1
|
4505
|
sub is_success { return( HTTP::Promise::Status->is_success( shift->code ) ); } |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
sub parse |
301
|
|
|
|
|
|
|
{ |
302
|
3
|
|
|
3
|
1
|
1733
|
my $self = shift( @_ ); |
303
|
3
|
|
|
|
|
7
|
my $str = shift( @_ ); |
304
|
3
|
100
|
33
|
|
|
637
|
warnings::warnif( 'Undefined argument to ' . ( ref( $self ) || $self ) . '->parse()' ) if( !defined( $str ) ); |
305
|
3
|
|
|
|
|
104
|
$self->clear_error; |
306
|
3
|
100
|
66
|
|
|
184
|
if( !defined( $str ) || !length( $str ) ) |
307
|
|
|
|
|
|
|
{ |
308
|
2
|
50
|
|
|
|
13
|
return( ref( $self ) ? $self : $self->new ); |
309
|
|
|
|
|
|
|
} |
310
|
1
|
|
|
|
|
18
|
$str = $self->new_scalar( $str ); |
311
|
1
|
50
|
|
|
|
30
|
$self->_load_class( 'HTTP::Promise::Parser' ) || return( $self->pass_error ); |
312
|
1
|
|
|
|
|
56
|
my $p = HTTP::Promise::Parser->new; |
313
|
1
|
|
50
|
|
|
40
|
my $ent = $p->parse( $str ) || return( $self->pass_error( $p->error ) ); |
314
|
1
|
|
|
|
|
393
|
return( $ent->http_message ); |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
16
|
|
|
16
|
1
|
36664
|
sub previous { return( shift->_set_get_object_without_init( 'previous', 'HTTP::Promise::Message', @_ ) ); } |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
sub redirects |
320
|
|
|
|
|
|
|
{ |
321
|
4
|
|
|
4
|
1
|
37044
|
my $self = shift( @_ ); |
322
|
4
|
|
|
|
|
15
|
my $reds = $self->new_array; |
323
|
4
|
|
|
|
|
93
|
my $r = $self; |
324
|
4
|
|
|
|
|
15
|
while( my $p = $r->previous ) |
325
|
|
|
|
|
|
|
{ |
326
|
5
|
|
|
|
|
125
|
$reds->unshift( $p ); |
327
|
5
|
|
|
|
|
38
|
$r = $p; |
328
|
|
|
|
|
|
|
} |
329
|
4
|
|
|
|
|
121
|
return( $reds ); |
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
|
332
|
25
|
|
|
25
|
1
|
1530
|
sub request { return( shift->_set_get_object_without_init( 'request', 'HTTP::Promise::Request', @_ ) ); } |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
# See rfc7230, section 3.1 <https://tools.ietf.org/html/rfc7230#section-3.1> |
335
|
|
|
|
|
|
|
sub start_line |
336
|
|
|
|
|
|
|
{ |
337
|
4
|
|
|
4
|
1
|
13
|
my $self = shift( @_ ); |
338
|
4
|
|
|
|
|
11
|
my $eol = shift( @_ ); |
339
|
4
|
50
|
|
|
|
45
|
$eol = "\n" if( !defined( $eol ) ); |
340
|
4
|
|
|
|
|
20
|
my $status_line = $self->status_line; |
341
|
4
|
|
100
|
|
|
73
|
my $proto = $self->protocol || 'HTTP/1.1'; |
342
|
4
|
|
|
|
|
3292
|
my $resp_line = "$proto $status_line"; |
343
|
4
|
|
|
|
|
35
|
return( $resp_line ); |
344
|
|
|
|
|
|
|
} |
345
|
|
|
|
|
|
|
|
346
|
15
|
|
|
15
|
1
|
96015
|
sub status { return( shift->_set_get_scalar_as_object( 'status', @_ ) ); } |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
sub status_line |
349
|
|
|
|
|
|
|
{ |
350
|
6
|
|
|
6
|
1
|
958
|
my $self = shift( @_ ); |
351
|
6
|
|
100
|
|
|
20
|
my $code = $self->code || "000"; |
352
|
6
|
|
100
|
|
|
40740
|
my $status = $self->status || HTTP::Promise::Status->status_message( $code ) || 'Unknown code'; |
353
|
6
|
|
|
|
|
2536
|
return( "$code $status" ); |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
# NOTE: sub FREEZE is inherited |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
# NOTE: sub STORABLE_freeze is inherited |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
# NOTE: sub STORABLE_thaw is inherited |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
# NOTE: sub THAW is inherited |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
1; |
365
|
|
|
|
|
|
|
# NOTE: POD |
366
|
|
|
|
|
|
|
__END__ |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
=encoding utf-8 |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
=head1 NAME |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
HTTP::Promise::Response - HTTP Response Class |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
=head1 SYNOPSIS |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
use HTTP::Promise::Response; |
377
|
|
|
|
|
|
|
my $resp = HTTP::Promise::Response->new || |
378
|
|
|
|
|
|
|
die( HTTP::Promise::Response->error, "\n" ); |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
=head1 VERSION |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
v0.1.0 |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=head1 DESCRIPTION |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
L<HTTP::Promise::Response> implements a similar interface to L<HTTP::Response>, but does not inherit from it. It uses a different API internally and relies on XS modules for speed while offering more features. |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
L<HTTP::Promise::Response> inherits from L<HTTP::Promise::Message> |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
One major difference with C<HTTP::Response> is that the HTTP response content is not necessarily stored in memory, but it relies on L<HTTP::Promise::Body> as you can see below, and this class has 2 subclasses: 1 storing data in memory when the size is reasonable (threshold set by you) and 1 storing data in a file on the filesystem for larger content. |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
Here is how it fits in overall relation with other classes. |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
+-------------------------+ +--------------------------+ |
395
|
|
|
|
|
|
|
| | | | |
396
|
|
|
|
|
|
|
| HTTP::Promise::Request | | HTTP::Promise::Response | |
397
|
|
|
|
|
|
|
| | | | |
398
|
|
|
|
|
|
|
+------------|------------+ +-------------|------------+ |
399
|
|
|
|
|
|
|
| | |
400
|
|
|
|
|
|
|
| | |
401
|
|
|
|
|
|
|
| | |
402
|
|
|
|
|
|
|
| +------------------------+ | |
403
|
|
|
|
|
|
|
| | | | |
404
|
|
|
|
|
|
|
+--- HTTP::Promise::Message |---+ |
405
|
|
|
|
|
|
|
| | |
406
|
|
|
|
|
|
|
+------------|-----------+ |
407
|
|
|
|
|
|
|
| |
408
|
|
|
|
|
|
|
| |
409
|
|
|
|
|
|
|
+------------|-----------+ |
410
|
|
|
|
|
|
|
| | |
411
|
|
|
|
|
|
|
| HTTP::Promise::Entity | |
412
|
|
|
|
|
|
|
| | |
413
|
|
|
|
|
|
|
+------------|-----------+ |
414
|
|
|
|
|
|
|
| |
415
|
|
|
|
|
|
|
| |
416
|
|
|
|
|
|
|
+------------|-----------+ |
417
|
|
|
|
|
|
|
| | |
418
|
|
|
|
|
|
|
| HTTP::Promise::Body | |
419
|
|
|
|
|
|
|
| | |
420
|
|
|
|
|
|
|
+------------------------+ |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
=head1 CONSTRUCTOR |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
=head2 new |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
my $resp = HTTP::Promise::Response->new( $code, $status, $headers, $content, |
427
|
|
|
|
|
|
|
host => 'example.com', |
428
|
|
|
|
|
|
|
uri => 'https://example.com/somewhere', |
429
|
|
|
|
|
|
|
); |
430
|
|
|
|
|
|
|
my $resp = HTTP::Promise::Response->new( $code, $status, $headers, $content, { |
431
|
|
|
|
|
|
|
host => 'example.com', |
432
|
|
|
|
|
|
|
uri => 'https://example.com/somewhere', |
433
|
|
|
|
|
|
|
}); |
434
|
|
|
|
|
|
|
my $resp = HTTP::Promise::Response->new( $code, $status, $headers, |
435
|
|
|
|
|
|
|
host => 'example.com', |
436
|
|
|
|
|
|
|
uri => 'https://example.com/somewhere', |
437
|
|
|
|
|
|
|
); |
438
|
|
|
|
|
|
|
my $resp = HTTP::Promise::Response->new( $code, $status, $headers, { |
439
|
|
|
|
|
|
|
host => 'example.com', |
440
|
|
|
|
|
|
|
uri => 'https://example.com/somewhere', |
441
|
|
|
|
|
|
|
}); |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
Provided with an HTTP code, HTTP status, an optional set of headers, as either an array reference or a L<HTTP::Promise::Headers> or a L<HTTP::Headers> object, some optional content and an optional hash reference of options (as the last or only parameter), and this instantiates a new L<HTTP::Promise::Response> object. The supported arguments are as follow. Each arguments can be set or changed later using the method with the same name. |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
It returns the newly created object upon success, and upon error, such as bad argument provided, this sets an L<error|Module::Generic/error> and returns C<undef> |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=over 4 |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
=item 1. C<$code> |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
An integer representing the status code, such as C<101> (switching protocol). |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
=item 2. C<$status> |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
The status string, such as C<Switching Protocol>. |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
=item 3. C<$headers> |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
Either an array reference of header-value pairs, or an L<HTTP::Promise::Headers> object or an L<HTTP::Headers> object. |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
If an array reference is provided, an L<HTTP::Promise::Headers> object will be instantiated with it. |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
For example:: |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
my $r = HTTP::Promise::Response->new( $code, $status, [ |
466
|
|
|
|
|
|
|
'Content-Type' => 'text/html; charset=utf-8', |
467
|
|
|
|
|
|
|
Content_Encoding => 'gzip', |
468
|
|
|
|
|
|
|
]); |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
=item 4. C<$content> |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
C<$content> can either be a string, a scalar reference, or an L<HTTP::Promise::Body> object (L<HTTP::Promise::Body::File> and L<HTTP::Promise::Body::Scalar>) |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
=back |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
Each supported option below can also be set using its corresponding method. |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
Supported options are: |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
=over 4 |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
=item * C<code> |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
Same as C<$code> above. |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
=item * C<content> |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
Same as C<$content> above. |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
=item * C<headers> |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
Same as C<$headers> above. |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
=item * C<protocol> |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
The HTTP protocol, such as C<HTTP/1.1> or C<HTTP/2> |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
=item * C<status> |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
Same as C<$status> above. |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
=item * C<version> |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
The HTTP protocol version. Defaults to C<1.17> |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
=back |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
=head1 METHODS |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
=head2 add_content |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/add_content> |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
=head2 add_content_utf8 |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/add_content_utf8> |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
=head2 add_part |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/add_part> |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
=head2 as_string |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/as_string> |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=head2 base |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
Returns the base URI as an L<URI> object if it can find one, or C<undef> otherwise. |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=head2 boundary |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message> and returns the multipart boundary currently set in the C<Content-Type> header. |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
=head2 can |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/can> |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
=head2 clear |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/clear> |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
=head2 clone |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
This clones the current object and returns the clone version. |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
=head2 code |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
Sets or gets the HTTP response C<code>. This returns a L<number object|Module::Generic::Number> |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
=head2 content |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/content> |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
Use this method with care, because it will stringify the request body, thus loading it into memory, which could potentially be important if the body size is large. Maybe you can check the body size first? Something like: |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
my $content; |
557
|
|
|
|
|
|
|
$content = $r->content if( $r->body->length < 102400 ); |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
=head2 content_charset |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/content_charset> |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
=head2 content_ref |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/content_ref> |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
=head2 current_age |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
Calculates the "current age" of the response as specified by L<rfc2616, section 13.2.3|https://tools.ietf.org/html/rfc2616#section-13.2.3>. |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
The age of a response is the time since it was sent by the origin server. |
572
|
|
|
|
|
|
|
The returned value is a number representing the age in seconds. |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
=head2 decodable |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/decodable> |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
=head2 decode |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/decode> |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
=head2 decode_content |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/decode_content> |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
=head2 decoded_content |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/decoded_content> |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
=head2 decoded_content_utf8 |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/decoded_content_utf8> |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
=head2 default_protocol |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
Sets or gets the default HTTP protocol to use. This defaults to C<HTTP/1.0> |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
=head2 dump |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
This dumps the HTTP response and prints it on the C<STDOUT> in void context, or returns a string of it. |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
=head2 encode |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/encode> |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
=head2 entity |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
Sets or gets an L<HTTP::Promise::Entity> object. |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
This object is automatically created upon instantiation of the HTTP request, and if you also provide some content when creating a new object, an L<HTTP::Promise::Body> object will also be created. |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
=head2 filename |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
Returns a possible filename, if any, for this response. |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
To achieve this, it tries different approaches: |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
=over 4 |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
=item 1. C<Content-Disposition> header |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
It will check in the C<Content-Disposition> header of the response to see if there ia a C<filename> attribute, or a C<filename*> attribute (as defined in L<rfc2231|https://tools.ietf.org/html/rfc2231>) |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
For example: |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
Content-Disposition: form-data; name="myfile"; filename*=UTF-8''%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.txt |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
or |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
Content-Disposition: form-data; name="myfile"; filename*=UTF-8'ja-JP'%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.txt |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
In the above example, note the C<*> after the attribute name C<filename>. It is not a typo and part of the L<rfc2231 standard|https://tools.ietf.org/html/rfc2231> |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
or encoded with quoted-printable |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
Content-Disposition: attachment; filename="=?UTF-8?Q?=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB.txt?=" |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
or encoded with base64 |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
Content-Disposition: attachment; filename="=?UTF-8?B?44OV44Kh44Kk44OrLnR4dAo?=" |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
Here the filename would be C<ファイル.txt> (i.e. "file.txt" in Japanese) |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
=item 2. C<Content-Location> header |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
It will use the base filename of the URI. |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
=item 3. C<request URI> |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
If there was an initial request URI, it will use the URI base filename. |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
This might not be the original request URI, because there might have been some redirect responses first. |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
=back |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
Whatever filename found is returned as-is. You need to be careful there are no dangerous characters in it before relying on it as part of a filepath. |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
If nothing is found, C<undef> is returned. |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
=head2 fresh_until |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
Returns the time (in seconds since epoch) when this entity is no longer fresh. |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
Options might be passed to control expiry heuristics. See the description of L</freshness_lifetime>. |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
=head2 freshness_lifetime |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
Calculates the "freshness lifetime" of the response as specified by L<rfc2616, section 13.2.4|https://tools.ietf.org/html/rfc2616#section-13.2.4> and updated by L<rfc7234, section 4.2|https://tools.ietf.org/html/rfc7234#section-4.2>. |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
The "freshness lifetime" is the length of time between the generation of a response and its expiration time. |
671
|
|
|
|
|
|
|
The returned value is the number of seconds until expiry. |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
If the response does not contain an C<Expires> or a C<Cache-Control> header, then this function will apply some simple heuristic based on the C<Last-Modified> header to determine a suitable lifetime. The following options might be passed to control the heuristics: |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
=over 4 |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
=item * C<heuristic_expiry> |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
Boolean. If set to a false value, do not apply heuristics and just return C<undef> when C<Expires> or C<Cache-Control> field is lacking. |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
=item * C<h_lastmod_fraction> |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
Integer. This number represent the fraction of the difference since the C<Last-Modified> timestamp to make the expiry time. |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
The default is C<0.10>, the suggested typical setting of 10% in L<rfc2616|https://tools.ietf.org/html/rfc2616>. |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
=item * C<h_min> |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
Integer representing seconds. This is the lower limit of the heuristic expiry age to use. |
690
|
|
|
|
|
|
|
The default is C<60> (1 minute). |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
=item * C<h_max> |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
Integer representing seconds. This is the upper limit of the heuristic expiry age to use. |
695
|
|
|
|
|
|
|
The default is C<86400> (24 hours). |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
=item * C<h_default> |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
Integer representing seconds. This is the expiry age to use when nothing else applies. |
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
The default is C<3600> (1 hour) or C<h_min> if greater. |
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
=back |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
=head2 header |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/header> |
708
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
=head2 headers |
710
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
Sets or gets a L<HTTP::Promise::Headers> object. |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
A header object is always created upon instantiation, whether you provided headers fields or not. |
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
=head2 headers_as_string |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/headers_as_string> |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
=head2 is_client_error |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
Returns true if the L</code> corresponds to a client error, which typically is a code from C<400> to C<499>, or false otherwise. |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
See also L<HTTP::Promise::Status/is_client_error> |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
=head2 is_encoding_supported |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/is_encoding_supported> |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
=head2 is_error |
730
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
Returns true if the L</code> corresponds to an error (client error or server error), which typically is a code from C<400> to C<599>, or false otherwise. |
732
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
See also L<HTTP::Promise::Status/is_error> |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
=head2 is_fresh |
736
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
Returns true if the response is fresh, based on the values of L</freshness_lifetime> and L</current_age>. |
738
|
|
|
|
|
|
|
If the response is no longer fresh, then it has to be re-fetched or re-validated by the origin server. |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
Options might be passed to control expiry heuristics, see the description of L</freshness_lifetime>. |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
=head2 is_info |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
Returns true if the L</code> corresponds to an informational code, which typically is a code from C<100> to C<199>, or false otherwise. |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
See also L<HTTP::Promise::Status/is_info> |
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
=head2 is_redirect |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
Returns true if the L</code> corresponds to a redirection, which typically is a code from C<300> to C<399>, or false otherwise. |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
See also L<HTTP::Promise::Status/is_redirect> |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
=head2 is_server_error |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
Returns true if the L</code> corresponds to a server error, which typically is a code from C<500> to C<599>, or false otherwise. |
757
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
See also L<HTTP::Promise::Status/is_server_error> |
759
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
=head2 is_success |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
Returns true if the L</code> corresponds to a successful response, which typically is a code from C<200> to C<299>, or false otherwise. |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
See also L<HTTP::Promise::Status/is_success> |
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
=head2 make_boundary |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/make_boundary> |
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
=head2 parse |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
Provided with a scalar reference of data, a glob or a file path, and an hash or hash reference of options and this will parse the data provided using L<HTTP::Promise::Parser/parse>, passing it whatever options has been provided. See L<HTTP::Promise::Parser/parse_fh> for the supported options. |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
This returns the resulting L<HTTP::Promise::Message> object from the parsing, or, upon error, sets an L<error|Module::Generic/error> and returns C<undef>. |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
Note that the resulting L<HTTP::Promise::Message> object can be a L<HTTP::Promise::Request> or L<HTTP::Promise::Response> object (both of which inherits from L<HTTP::Promise::Message>) if a start-line was found, or else just an L<HTTP::Promise::Message> object. |
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
=head2 parts |
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/parts> |
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
=head2 previous |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
Sets or gets an L<HTTP::Promise::Message> object corresponding to the previous HTTP query. This is used to keep track of redirection. |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
=head2 protocol |
787
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/protocol> |
789
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
=head2 redirects |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
Returns an L<array object|Module::Generic::Array> of redirect responses that lead up to this response by following the C<$r->previous> chain. The list order is oldest first. |
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
For example: |
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
my $reds = $r->redirects; |
797
|
|
|
|
|
|
|
say "Number of redirects: ", $reds->length; |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
=head2 request |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
Sets or gets the L<HTTP::Promise::Request> related to this response. |
802
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
It is not necessarily the same request passed to the L<HTTP::Promise/request>, because there might have been redirects and authorisation retries in between. |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
=head2 start_line |
806
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
Returns a string representing the start-line containing the L<protocol|/protocol>, the L<code|/code> and the L<status|/status> of the response. |
808
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
For example: |
810
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
GET / HTTP/1.1 |
812
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
See L<rfc7230, section 3.1|https://tools.ietf.org/html/rfc7230#section-3.1> |
814
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
=head2 status |
816
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
Sets or gets the response status string, such as C<OK> for code C<200>. This returns a L<scalar object|Module::Generic::Scalar> |
818
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
=head2 status_line |
820
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
Returns a regular string made of the L</code> and the L</status>. If no status is set, this will guess it from L<HTTP::Promise::Status/status_message> |
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
=head2 version |
824
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/version> |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
=head1 AUTHOR |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
Jacques Deguest E<lt>F<jack@deguest.jp>E<gt> |
830
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
=head1 SEE ALSO |
832
|
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
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> |
834
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
836
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
Copyright(c) 2022 DEGUEST Pte. Ltd. |
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
All rights reserved |
840
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
=cut |