Filename | /usr/share/perl5/HTTP/Message.pm |
Statements | Executed 18 statements in 5.14ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 26µs | 35µs | BEGIN@3 | HTTP::Message::
1 | 1 | 1 | 22µs | 56µs | BEGIN@643 | HTTP::Message::
1 | 1 | 1 | 11µs | 52µs | BEGIN@4 | HTTP::Message::
0 | 0 | 0 | 0s | 0s | AUTOLOAD | HTTP::Message::
0 | 0 | 0 | 0s | 0s | DESTROY | HTTP::Message::
0 | 0 | 0 | 0s | 0s | __ANON__[:18] | HTTP::Message::
0 | 0 | 0 | 0s | 0s | __ANON__[:21] | HTTP::Message::
0 | 0 | 0 | 0s | 0s | __ANON__[:261] | HTTP::Message::
0 | 0 | 0 | 0s | 0s | __ANON__[:644] | HTTP::Message::
0 | 0 | 0 | 0s | 0s | _boundary | HTTP::Message::
0 | 0 | 0 | 0s | 0s | _content | HTTP::Message::
0 | 0 | 0 | 0s | 0s | _elem | HTTP::Message::
0 | 0 | 0 | 0s | 0s | _parts | HTTP::Message::
0 | 0 | 0 | 0s | 0s | _set_content | HTTP::Message::
0 | 0 | 0 | 0s | 0s | _stale_content | HTTP::Message::
0 | 0 | 0 | 0s | 0s | add_content | HTTP::Message::
0 | 0 | 0 | 0s | 0s | add_content_utf8 | HTTP::Message::
0 | 0 | 0 | 0s | 0s | add_part | HTTP::Message::
0 | 0 | 0 | 0s | 0s | as_string | HTTP::Message::
0 | 0 | 0 | 0s | 0s | clear | HTTP::Message::
0 | 0 | 0 | 0s | 0s | clone | HTTP::Message::
0 | 0 | 0 | 0s | 0s | content | HTTP::Message::
0 | 0 | 0 | 0s | 0s | content_charset | HTTP::Message::
0 | 0 | 0 | 0s | 0s | content_ref | HTTP::Message::
0 | 0 | 0 | 0s | 0s | decodable | HTTP::Message::
0 | 0 | 0 | 0s | 0s | decode | HTTP::Message::
0 | 0 | 0 | 0s | 0s | decoded_content | HTTP::Message::
0 | 0 | 0 | 0s | 0s | dump | HTTP::Message::
0 | 0 | 0 | 0s | 0s | encode | HTTP::Message::
0 | 0 | 0 | 0s | 0s | headers | HTTP::Message::
0 | 0 | 0 | 0s | 0s | headers_as_string | HTTP::Message::
0 | 0 | 0 | 0s | 0s | new | HTTP::Message::
0 | 0 | 0 | 0s | 0s | parse | HTTP::Message::
0 | 0 | 0 | 0s | 0s | parts | HTTP::Message::
0 | 0 | 0 | 0s | 0s | protocol | HTTP::Message::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | package HTTP::Message; | ||||
2 | |||||
3 | 3 | 53µs | 2 | 44µs | # spent 35µs (26+9) within HTTP::Message::BEGIN@3 which was called:
# once (26µs+9µs) by LWP::UserAgent::BEGIN@10 at line 3 # spent 35µs making 1 call to HTTP::Message::BEGIN@3
# spent 9µs making 1 call to strict::import |
4 | 3 | 4.07ms | 2 | 93µs | # spent 52µs (11+41) within HTTP::Message::BEGIN@4 which was called:
# once (11µs+41µs) by LWP::UserAgent::BEGIN@10 at line 4 # spent 52µs making 1 call to HTTP::Message::BEGIN@4
# spent 41µs making 1 call to vars::import |
5 | 1 | 1µs | $VERSION = "5.835"; | ||
6 | |||||
7 | 1 | 140µs | require HTTP::Headers; | ||
8 | 1 | 2µs | require Carp; | ||
9 | |||||
10 | 1 | 2µs | my $CRLF = "\015\012"; # "\r\n" is not portable | ||
11 | 1 | 4µs | $HTTP::URI_CLASS ||= $ENV{PERL_HTTP_URI_CLASS} || "URI"; | ||
12 | 2 | 48µs | eval "require $HTTP::URI_CLASS"; die $@ if $@; # spent 113µs executing statements in string eval | ||
13 | |||||
14 | *_utf8_downgrade = defined(&utf8::downgrade) ? | ||||
15 | sub { | ||||
16 | utf8::downgrade($_[0], 1) or | ||||
17 | Carp::croak("HTTP::Message content must be bytes") | ||||
18 | } | ||||
19 | : | ||||
20 | sub { | ||||
21 | 1 | 8µs | }; | ||
22 | |||||
23 | sub new | ||||
24 | { | ||||
25 | my($class, $header, $content) = @_; | ||||
26 | if (defined $header) { | ||||
27 | Carp::croak("Bad header argument") unless ref $header; | ||||
28 | if (ref($header) eq "ARRAY") { | ||||
29 | $header = HTTP::Headers->new(@$header); | ||||
30 | } | ||||
31 | else { | ||||
32 | $header = $header->clone; | ||||
33 | } | ||||
34 | } | ||||
35 | else { | ||||
36 | $header = HTTP::Headers->new; | ||||
37 | } | ||||
38 | if (defined $content) { | ||||
39 | _utf8_downgrade($content); | ||||
40 | } | ||||
41 | else { | ||||
42 | $content = ''; | ||||
43 | } | ||||
44 | |||||
45 | bless { | ||||
46 | '_headers' => $header, | ||||
47 | '_content' => $content, | ||||
48 | }, $class; | ||||
49 | } | ||||
50 | |||||
51 | |||||
52 | sub parse | ||||
53 | { | ||||
54 | my($class, $str) = @_; | ||||
55 | |||||
56 | my @hdr; | ||||
57 | while (1) { | ||||
58 | if ($str =~ s/^([^\s:]+)[ \t]*: ?(.*)\n?//) { | ||||
59 | push(@hdr, $1, $2); | ||||
60 | $hdr[-1] =~ s/\r\z//; | ||||
61 | } | ||||
62 | elsif (@hdr && $str =~ s/^([ \t].*)\n?//) { | ||||
63 | $hdr[-1] .= "\n$1"; | ||||
64 | $hdr[-1] =~ s/\r\z//; | ||||
65 | } | ||||
66 | else { | ||||
67 | $str =~ s/^\r?\n//; | ||||
68 | last; | ||||
69 | } | ||||
70 | } | ||||
71 | local $HTTP::Headers::TRANSLATE_UNDERSCORE; | ||||
72 | new($class, \@hdr, $str); | ||||
73 | } | ||||
74 | |||||
75 | |||||
76 | sub clone | ||||
77 | { | ||||
78 | my $self = shift; | ||||
79 | my $clone = HTTP::Message->new($self->headers, | ||||
80 | $self->content); | ||||
81 | $clone->protocol($self->protocol); | ||||
82 | $clone; | ||||
83 | } | ||||
84 | |||||
85 | |||||
86 | sub clear { | ||||
87 | my $self = shift; | ||||
88 | $self->{_headers}->clear; | ||||
89 | $self->content(""); | ||||
90 | delete $self->{_parts}; | ||||
91 | return; | ||||
92 | } | ||||
93 | |||||
94 | |||||
95 | sub protocol { | ||||
96 | shift->_elem('_protocol', @_); | ||||
97 | } | ||||
98 | |||||
99 | sub headers { | ||||
100 | my $self = shift; | ||||
101 | |||||
102 | # recalculation of _content might change headers, so we | ||||
103 | # need to force it now | ||||
104 | $self->_content unless exists $self->{_content}; | ||||
105 | |||||
106 | $self->{_headers}; | ||||
107 | } | ||||
108 | |||||
109 | sub headers_as_string { | ||||
110 | shift->headers->as_string(@_); | ||||
111 | } | ||||
112 | |||||
113 | |||||
114 | sub content { | ||||
115 | |||||
116 | my $self = $_[0]; | ||||
117 | if (defined(wantarray)) { | ||||
118 | $self->_content unless exists $self->{_content}; | ||||
119 | my $old = $self->{_content}; | ||||
120 | $old = $$old if ref($old) eq "SCALAR"; | ||||
121 | &_set_content if @_ > 1; | ||||
122 | return $old; | ||||
123 | } | ||||
124 | |||||
125 | if (@_ > 1) { | ||||
126 | &_set_content; | ||||
127 | } | ||||
128 | else { | ||||
129 | Carp::carp("Useless content call in void context") if $^W; | ||||
130 | } | ||||
131 | } | ||||
132 | |||||
133 | |||||
134 | sub _set_content { | ||||
135 | my $self = $_[0]; | ||||
136 | _utf8_downgrade($_[1]); | ||||
137 | if (!ref($_[1]) && ref($self->{_content}) eq "SCALAR") { | ||||
138 | ${$self->{_content}} = $_[1]; | ||||
139 | } | ||||
140 | else { | ||||
141 | die "Can't set content to be a scalar reference" if ref($_[1]) eq "SCALAR"; | ||||
142 | $self->{_content} = $_[1]; | ||||
143 | delete $self->{_content_ref}; | ||||
144 | } | ||||
145 | delete $self->{_parts} unless $_[2]; | ||||
146 | } | ||||
147 | |||||
148 | |||||
149 | sub add_content | ||||
150 | { | ||||
151 | my $self = shift; | ||||
152 | $self->_content unless exists $self->{_content}; | ||||
153 | my $chunkref = \$_[0]; | ||||
154 | $chunkref = $$chunkref if ref($$chunkref); # legacy | ||||
155 | |||||
156 | _utf8_downgrade($$chunkref); | ||||
157 | |||||
158 | my $ref = ref($self->{_content}); | ||||
159 | if (!$ref) { | ||||
160 | $self->{_content} .= $$chunkref; | ||||
161 | } | ||||
162 | elsif ($ref eq "SCALAR") { | ||||
163 | ${$self->{_content}} .= $$chunkref; | ||||
164 | } | ||||
165 | else { | ||||
166 | Carp::croak("Can't append to $ref content"); | ||||
167 | } | ||||
168 | delete $self->{_parts}; | ||||
169 | } | ||||
170 | |||||
171 | sub add_content_utf8 { | ||||
172 | my($self, $buf) = @_; | ||||
173 | utf8::upgrade($buf); | ||||
174 | utf8::encode($buf); | ||||
175 | $self->add_content($buf); | ||||
176 | } | ||||
177 | |||||
178 | sub content_ref | ||||
179 | { | ||||
180 | my $self = shift; | ||||
181 | $self->_content unless exists $self->{_content}; | ||||
182 | delete $self->{_parts}; | ||||
183 | my $old = \$self->{_content}; | ||||
184 | my $old_cref = $self->{_content_ref}; | ||||
185 | if (@_) { | ||||
186 | my $new = shift; | ||||
187 | Carp::croak("Setting content_ref to a non-ref") unless ref($new); | ||||
188 | delete $self->{_content}; # avoid modifying $$old | ||||
189 | $self->{_content} = $new; | ||||
190 | $self->{_content_ref}++; | ||||
191 | } | ||||
192 | $old = $$old if $old_cref; | ||||
193 | return $old; | ||||
194 | } | ||||
195 | |||||
196 | |||||
197 | sub content_charset | ||||
198 | { | ||||
199 | my $self = shift; | ||||
200 | if (my $charset = $self->content_type_charset) { | ||||
201 | return $charset; | ||||
202 | } | ||||
203 | |||||
204 | # time to start guessing | ||||
205 | my $cref = $self->decoded_content(ref => 1, charset => "none"); | ||||
206 | |||||
207 | # Unicode BOM | ||||
208 | for ($$cref) { | ||||
209 | return "UTF-8" if /^\xEF\xBB\xBF/; | ||||
210 | return "UTF-32-LE" if /^\xFF\xFE\x00\x00/; | ||||
211 | return "UTF-32-BE" if /^\x00\x00\xFE\xFF/; | ||||
212 | return "UTF-16-LE" if /^\xFF\xFE/; | ||||
213 | return "UTF-16-BE" if /^\xFE\xFF/; | ||||
214 | } | ||||
215 | |||||
216 | if ($self->content_is_xml) { | ||||
217 | # http://www.w3.org/TR/2006/REC-xml-20060816/#sec-guessing | ||||
218 | # XML entity not accompanied by external encoding information and not | ||||
219 | # in UTF-8 or UTF-16 encoding must begin with an XML encoding declaration, | ||||
220 | # in which the first characters must be '<?xml' | ||||
221 | for ($$cref) { | ||||
222 | return "UTF-32-BE" if /^\x00\x00\x00</; | ||||
223 | return "UTF-32-LE" if /^<\x00\x00\x00/; | ||||
224 | return "UTF-16-BE" if /^(?:\x00\s)*\x00</; | ||||
225 | return "UTF-16-LE" if /^(?:\s\x00)*<\x00/; | ||||
226 | if (/^\s*(<\?xml[^\x00]*?\?>)/) { | ||||
227 | if ($1 =~ /\sencoding\s*=\s*(["'])(.*?)\1/) { | ||||
228 | my $enc = $2; | ||||
229 | $enc =~ s/^\s+//; $enc =~ s/\s+\z//; | ||||
230 | return $enc if $enc; | ||||
231 | } | ||||
232 | } | ||||
233 | } | ||||
234 | return "UTF-8"; | ||||
235 | } | ||||
236 | elsif ($self->content_is_html) { | ||||
237 | # look for <META charset="..."> or <META content="..."> | ||||
238 | # http://dev.w3.org/html5/spec/Overview.html#determining-the-character-encoding | ||||
239 | my $charset; | ||||
240 | require HTML::Parser; | ||||
241 | my $p = HTML::Parser->new( | ||||
242 | start_h => [sub { | ||||
243 | my($tag, $attr, $self) = @_; | ||||
244 | $charset = $attr->{charset}; | ||||
245 | unless ($charset) { | ||||
246 | # look at $attr->{content} ... | ||||
247 | if (my $c = $attr->{content}) { | ||||
248 | require HTTP::Headers::Util; | ||||
249 | my @v = HTTP::Headers::Util::split_header_words($c); | ||||
250 | return unless @v; | ||||
251 | my($ct, undef, %ct_param) = @{$v[0]}; | ||||
252 | $charset = $ct_param{charset}; | ||||
253 | } | ||||
254 | return unless $charset; | ||||
255 | } | ||||
256 | if ($charset =~ /^utf-?16/i) { | ||||
257 | # converted document, assume UTF-8 | ||||
258 | $charset = "UTF-8"; | ||||
259 | } | ||||
260 | $self->eof; | ||||
261 | }, "tagname, attr, self"], | ||||
262 | report_tags => [qw(meta)], | ||||
263 | utf8_mode => 1, | ||||
264 | ); | ||||
265 | $p->parse($$cref); | ||||
266 | return $charset if $charset; | ||||
267 | } | ||||
268 | if ($self->content_type =~ /^text\//) { | ||||
269 | for ($$cref) { | ||||
270 | if (length) { | ||||
271 | return "US-ASCII" unless /[\x80-\xFF]/; | ||||
272 | require Encode; | ||||
273 | eval { | ||||
274 | Encode::decode_utf8($_, Encode::FB_CROAK()); | ||||
275 | }; | ||||
276 | return "UTF-8" unless $@; | ||||
277 | return "ISO-8859-1"; | ||||
278 | } | ||||
279 | } | ||||
280 | } | ||||
281 | |||||
282 | return undef; | ||||
283 | } | ||||
284 | |||||
285 | |||||
286 | sub decoded_content | ||||
287 | { | ||||
288 | my($self, %opt) = @_; | ||||
289 | my $content_ref; | ||||
290 | my $content_ref_iscopy; | ||||
291 | |||||
292 | eval { | ||||
293 | $content_ref = $self->content_ref; | ||||
294 | die "Can't decode ref content" if ref($content_ref) ne "SCALAR"; | ||||
295 | |||||
296 | if (my $h = $self->header("Content-Encoding")) { | ||||
297 | $h =~ s/^\s+//; | ||||
298 | $h =~ s/\s+$//; | ||||
299 | for my $ce (reverse split(/\s*,\s*/, lc($h))) { | ||||
300 | next unless $ce; | ||||
301 | next if $ce eq "identity"; | ||||
302 | if ($ce eq "gzip" || $ce eq "x-gzip") { | ||||
303 | require IO::Uncompress::Gunzip; | ||||
304 | my $output; | ||||
305 | IO::Uncompress::Gunzip::gunzip($content_ref, \$output, Transparent => 0) | ||||
306 | or die "Can't gunzip content: $IO::Uncompress::Gunzip::GunzipError"; | ||||
307 | $content_ref = \$output; | ||||
308 | $content_ref_iscopy++; | ||||
309 | } | ||||
310 | elsif ($ce eq "x-bzip2") { | ||||
311 | require IO::Uncompress::Bunzip2; | ||||
312 | my $output; | ||||
313 | IO::Uncompress::Bunzip2::bunzip2($content_ref, \$output, Transparent => 0) | ||||
314 | or die "Can't bunzip content: $IO::Uncompress::Bunzip2::Bunzip2Error"; | ||||
315 | $content_ref = \$output; | ||||
316 | $content_ref_iscopy++; | ||||
317 | } | ||||
318 | elsif ($ce eq "deflate") { | ||||
319 | require IO::Uncompress::Inflate; | ||||
320 | my $output; | ||||
321 | my $status = IO::Uncompress::Inflate::inflate($content_ref, \$output, Transparent => 0); | ||||
322 | my $error = $IO::Uncompress::Inflate::InflateError; | ||||
323 | unless ($status) { | ||||
324 | # "Content-Encoding: deflate" is supposed to mean the | ||||
325 | # "zlib" format of RFC 1950, but Microsoft got that | ||||
326 | # wrong, so some servers sends the raw compressed | ||||
327 | # "deflate" data. This tries to inflate this format. | ||||
328 | $output = undef; | ||||
329 | require IO::Uncompress::RawInflate; | ||||
330 | unless (IO::Uncompress::RawInflate::rawinflate($content_ref, \$output)) { | ||||
331 | $self->push_header("Client-Warning" => | ||||
332 | "Could not raw inflate content: $IO::Uncompress::RawInflate::RawInflateError"); | ||||
333 | $output = undef; | ||||
334 | } | ||||
335 | } | ||||
336 | die "Can't inflate content: $error" unless defined $output; | ||||
337 | $content_ref = \$output; | ||||
338 | $content_ref_iscopy++; | ||||
339 | } | ||||
340 | elsif ($ce eq "compress" || $ce eq "x-compress") { | ||||
341 | die "Can't uncompress content"; | ||||
342 | } | ||||
343 | elsif ($ce eq "base64") { # not really C-T-E, but should be harmless | ||||
344 | require MIME::Base64; | ||||
345 | $content_ref = \MIME::Base64::decode($$content_ref); | ||||
346 | $content_ref_iscopy++; | ||||
347 | } | ||||
348 | elsif ($ce eq "quoted-printable") { # not really C-T-E, but should be harmless | ||||
349 | require MIME::QuotedPrint; | ||||
350 | $content_ref = \MIME::QuotedPrint::decode($$content_ref); | ||||
351 | $content_ref_iscopy++; | ||||
352 | } | ||||
353 | else { | ||||
354 | die "Don't know how to decode Content-Encoding '$ce'"; | ||||
355 | } | ||||
356 | } | ||||
357 | } | ||||
358 | |||||
359 | if ($self->content_is_text || (my $is_xml = $self->content_is_xml)) { | ||||
360 | my $charset = lc( | ||||
361 | $opt{charset} || | ||||
362 | $self->content_type_charset || | ||||
363 | $opt{default_charset} || | ||||
364 | $self->content_charset || | ||||
365 | "ISO-8859-1" | ||||
366 | ); | ||||
367 | unless ($charset =~ /^(?:none|us-ascii|iso-8859-1)\z/) { | ||||
368 | require Encode; | ||||
369 | if (do{my $v = $Encode::VERSION; $v =~ s/_//g; $v} < 2.0901 && | ||||
370 | !$content_ref_iscopy) | ||||
371 | { | ||||
372 | # LEAVE_SRC did not work before Encode-2.0901 | ||||
373 | my $copy = $$content_ref; | ||||
374 | $content_ref = \$copy; | ||||
375 | $content_ref_iscopy++; | ||||
376 | } | ||||
377 | eval { | ||||
378 | $content_ref = \Encode::decode($charset, $$content_ref, | ||||
379 | ($opt{charset_strict} ? Encode::FB_CROAK() : 0) | Encode::LEAVE_SRC()); | ||||
380 | }; | ||||
381 | if ($@) { | ||||
382 | my $retried; | ||||
383 | if ($@ =~ /^Unknown encoding/) { | ||||
384 | my $alt_charset = lc($opt{alt_charset} || ""); | ||||
385 | if ($alt_charset && $charset ne $alt_charset) { | ||||
386 | # Retry decoding with the alternative charset | ||||
387 | $content_ref = \Encode::decode($alt_charset, $$content_ref, | ||||
388 | ($opt{charset_strict} ? Encode::FB_CROAK() : 0) | Encode::LEAVE_SRC()) | ||||
389 | unless $alt_charset =~ /^(?:none|us-ascii|iso-8859-1)\z/; | ||||
390 | $retried++; | ||||
391 | } | ||||
392 | } | ||||
393 | die unless $retried; | ||||
394 | } | ||||
395 | die "Encode::decode() returned undef improperly" unless defined $$content_ref; | ||||
396 | if ($is_xml) { | ||||
397 | # Get rid of the XML encoding declaration if present | ||||
398 | $$content_ref =~ s/^\x{FEFF}//; | ||||
399 | if ($$content_ref =~ /^(\s*<\?xml[^\x00]*?\?>)/) { | ||||
400 | substr($$content_ref, 0, length($1)) =~ s/\sencoding\s*=\s*(["']).*?\1//; | ||||
401 | } | ||||
402 | } | ||||
403 | } | ||||
404 | } | ||||
405 | }; | ||||
406 | if ($@) { | ||||
407 | Carp::croak($@) if $opt{raise_error}; | ||||
408 | return undef; | ||||
409 | } | ||||
410 | |||||
411 | return $opt{ref} ? $content_ref : $$content_ref; | ||||
412 | } | ||||
413 | |||||
414 | |||||
415 | sub decodable | ||||
416 | { | ||||
417 | # should match the Content-Encoding values that decoded_content can deal with | ||||
418 | my $self = shift; | ||||
419 | my @enc; | ||||
420 | # XXX preferably we should determine if the modules are available without loading | ||||
421 | # them here | ||||
422 | eval { | ||||
423 | require IO::Uncompress::Gunzip; | ||||
424 | push(@enc, "gzip", "x-gzip"); | ||||
425 | }; | ||||
426 | eval { | ||||
427 | require IO::Uncompress::Inflate; | ||||
428 | require IO::Uncompress::RawInflate; | ||||
429 | push(@enc, "deflate"); | ||||
430 | }; | ||||
431 | eval { | ||||
432 | require IO::Uncompress::Bunzip2; | ||||
433 | push(@enc, "x-bzip2"); | ||||
434 | }; | ||||
435 | # we don't care about announcing the 'identity', 'base64' and | ||||
436 | # 'quoted-printable' stuff | ||||
437 | return wantarray ? @enc : join(", ", @enc); | ||||
438 | } | ||||
439 | |||||
440 | |||||
441 | sub decode | ||||
442 | { | ||||
443 | my $self = shift; | ||||
444 | return 1 unless $self->header("Content-Encoding"); | ||||
445 | if (defined(my $content = $self->decoded_content(charset => "none"))) { | ||||
446 | $self->remove_header("Content-Encoding", "Content-Length", "Content-MD5"); | ||||
447 | $self->content($content); | ||||
448 | return 1; | ||||
449 | } | ||||
450 | return 0; | ||||
451 | } | ||||
452 | |||||
453 | |||||
454 | sub encode | ||||
455 | { | ||||
456 | my($self, @enc) = @_; | ||||
457 | |||||
458 | Carp::croak("Can't encode multipart/* messages") if $self->content_type =~ m,^multipart/,; | ||||
459 | Carp::croak("Can't encode message/* messages") if $self->content_type =~ m,^message/,; | ||||
460 | |||||
461 | return 1 unless @enc; # nothing to do | ||||
462 | |||||
463 | my $content = $self->content; | ||||
464 | for my $encoding (@enc) { | ||||
465 | if ($encoding eq "identity") { | ||||
466 | # nothing to do | ||||
467 | } | ||||
468 | elsif ($encoding eq "base64") { | ||||
469 | require MIME::Base64; | ||||
470 | $content = MIME::Base64::encode($content); | ||||
471 | } | ||||
472 | elsif ($encoding eq "gzip" || $encoding eq "x-gzip") { | ||||
473 | require IO::Compress::Gzip; | ||||
474 | my $output; | ||||
475 | IO::Compress::Gzip::gzip(\$content, \$output, Minimal => 1) | ||||
476 | or die "Can't gzip content: $IO::Compress::Gzip::GzipError"; | ||||
477 | $content = $output; | ||||
478 | } | ||||
479 | elsif ($encoding eq "deflate") { | ||||
480 | require IO::Compress::Deflate; | ||||
481 | my $output; | ||||
482 | IO::Compress::Deflate::deflate(\$content, \$output) | ||||
483 | or die "Can't deflate content: $IO::Compress::Deflate::DeflateError"; | ||||
484 | $content = $output; | ||||
485 | } | ||||
486 | elsif ($encoding eq "x-bzip2") { | ||||
487 | require IO::Compress::Bzip2; | ||||
488 | my $output; | ||||
489 | IO::Compress::Bzip2::bzip2(\$content, \$output) | ||||
490 | or die "Can't bzip2 content: $IO::Compress::Bzip2::Bzip2Error"; | ||||
491 | $content = $output; | ||||
492 | } | ||||
493 | elsif ($encoding eq "rot13") { # for the fun of it | ||||
494 | $content =~ tr/A-Za-z/N-ZA-Mn-za-m/; | ||||
495 | } | ||||
496 | else { | ||||
497 | return 0; | ||||
498 | } | ||||
499 | } | ||||
500 | my $h = $self->header("Content-Encoding"); | ||||
501 | unshift(@enc, $h) if $h; | ||||
502 | $self->header("Content-Encoding", join(", ", @enc)); | ||||
503 | $self->remove_header("Content-Length", "Content-MD5"); | ||||
504 | $self->content($content); | ||||
505 | return 1; | ||||
506 | } | ||||
507 | |||||
508 | |||||
509 | sub as_string | ||||
510 | { | ||||
511 | my($self, $eol) = @_; | ||||
512 | $eol = "\n" unless defined $eol; | ||||
513 | |||||
514 | # The calculation of content might update the headers | ||||
515 | # so we need to do that first. | ||||
516 | my $content = $self->content; | ||||
517 | |||||
518 | return join("", $self->{'_headers'}->as_string($eol), | ||||
519 | $eol, | ||||
520 | $content, | ||||
521 | (@_ == 1 && length($content) && | ||||
522 | $content !~ /\n\z/) ? "\n" : "", | ||||
523 | ); | ||||
524 | } | ||||
525 | |||||
526 | |||||
527 | sub dump | ||||
528 | { | ||||
529 | my($self, %opt) = @_; | ||||
530 | my $content = $self->content; | ||||
531 | my $chopped = 0; | ||||
532 | if (!ref($content)) { | ||||
533 | my $maxlen = $opt{maxlength}; | ||||
534 | $maxlen = 512 unless defined($maxlen); | ||||
535 | if ($maxlen && length($content) > $maxlen * 1.1 + 3) { | ||||
536 | $chopped = length($content) - $maxlen; | ||||
537 | $content = substr($content, 0, $maxlen) . "..."; | ||||
538 | } | ||||
539 | |||||
540 | $content =~ s/\\/\\\\/g; | ||||
541 | $content =~ s/\t/\\t/g; | ||||
542 | $content =~ s/\r/\\r/g; | ||||
543 | |||||
544 | # no need for 3 digits in escape for these | ||||
545 | $content =~ s/([\0-\11\13-\037])(?!\d)/sprintf('\\%o',ord($1))/eg; | ||||
546 | |||||
547 | $content =~ s/([\0-\11\13-\037\177-\377])/sprintf('\\x%02X',ord($1))/eg; | ||||
548 | $content =~ s/([^\12\040-\176])/sprintf('\\x{%X}',ord($1))/eg; | ||||
549 | |||||
550 | # remaining whitespace | ||||
551 | $content =~ s/( +)\n/("\\40" x length($1)) . "\n"/eg; | ||||
552 | $content =~ s/(\n+)\n/("\\n" x length($1)) . "\n"/eg; | ||||
553 | $content =~ s/\n\z/\\n/; | ||||
554 | |||||
555 | my $no_content = "(no content)"; | ||||
556 | if ($content eq $no_content) { | ||||
557 | # escape our $no_content marker | ||||
558 | $content =~ s/^(.)/sprintf('\\x%02X',ord($1))/eg; | ||||
559 | } | ||||
560 | elsif ($content eq "") { | ||||
561 | $content = "(no content)"; | ||||
562 | } | ||||
563 | } | ||||
564 | |||||
565 | my @dump; | ||||
566 | push(@dump, $opt{preheader}) if $opt{preheader}; | ||||
567 | push(@dump, $self->{_headers}->as_string, $content); | ||||
568 | push(@dump, "(+ $chopped more bytes not shown)") if $chopped; | ||||
569 | |||||
570 | my $dump = join("\n", @dump, ""); | ||||
571 | $dump =~ s/^/$opt{prefix}/gm if $opt{prefix}; | ||||
572 | |||||
573 | print $dump unless defined wantarray; | ||||
574 | return $dump; | ||||
575 | } | ||||
576 | |||||
577 | |||||
578 | sub parts { | ||||
579 | my $self = shift; | ||||
580 | if (defined(wantarray) && (!exists $self->{_parts} || ref($self->{_content}) eq "SCALAR")) { | ||||
581 | $self->_parts; | ||||
582 | } | ||||
583 | my $old = $self->{_parts}; | ||||
584 | if (@_) { | ||||
585 | my @parts = map { ref($_) eq 'ARRAY' ? @$_ : $_ } @_; | ||||
586 | my $ct = $self->content_type || ""; | ||||
587 | if ($ct =~ m,^message/,) { | ||||
588 | Carp::croak("Only one part allowed for $ct content") | ||||
589 | if @parts > 1; | ||||
590 | } | ||||
591 | elsif ($ct !~ m,^multipart/,) { | ||||
592 | $self->remove_content_headers; | ||||
593 | $self->content_type("multipart/mixed"); | ||||
594 | } | ||||
595 | $self->{_parts} = \@parts; | ||||
596 | _stale_content($self); | ||||
597 | } | ||||
598 | return @$old if wantarray; | ||||
599 | return $old->[0]; | ||||
600 | } | ||||
601 | |||||
602 | sub add_part { | ||||
603 | my $self = shift; | ||||
604 | if (($self->content_type || "") !~ m,^multipart/,) { | ||||
605 | my $p = HTTP::Message->new($self->remove_content_headers, | ||||
606 | $self->content("")); | ||||
607 | $self->content_type("multipart/mixed"); | ||||
608 | $self->{_parts} = []; | ||||
609 | if ($p->headers->header_field_names || $p->content ne "") { | ||||
610 | push(@{$self->{_parts}}, $p); | ||||
611 | } | ||||
612 | } | ||||
613 | elsif (!exists $self->{_parts} || ref($self->{_content}) eq "SCALAR") { | ||||
614 | $self->_parts; | ||||
615 | } | ||||
616 | |||||
617 | push(@{$self->{_parts}}, @_); | ||||
618 | _stale_content($self); | ||||
619 | return; | ||||
620 | } | ||||
621 | |||||
622 | sub _stale_content { | ||||
623 | my $self = shift; | ||||
624 | if (ref($self->{_content}) eq "SCALAR") { | ||||
625 | # must recalculate now | ||||
626 | $self->_content; | ||||
627 | } | ||||
628 | else { | ||||
629 | # just invalidate cache | ||||
630 | delete $self->{_content}; | ||||
631 | delete $self->{_content_ref}; | ||||
632 | } | ||||
633 | } | ||||
634 | |||||
635 | |||||
636 | # delegate all other method calls the the headers object. | ||||
637 | sub AUTOLOAD | ||||
638 | { | ||||
639 | my $method = substr($AUTOLOAD, rindex($AUTOLOAD, '::')+2); | ||||
640 | |||||
641 | # We create the function here so that it will not need to be | ||||
642 | # autoloaded the next time. | ||||
643 | 3 | 806µs | 2 | 90µs | # spent 56µs (22+34) within HTTP::Message::BEGIN@643 which was called:
# once (22µs+34µs) by LWP::UserAgent::BEGIN@10 at line 643 # spent 56µs making 1 call to HTTP::Message::BEGIN@643
# spent 34µs making 1 call to strict::unimport |
644 | *$method = sub { shift->headers->$method(@_) }; | ||||
645 | goto &$method; | ||||
646 | } | ||||
647 | |||||
648 | |||||
649 | sub DESTROY {} # avoid AUTOLOADing it | ||||
650 | |||||
651 | |||||
652 | # Private method to access members in %$self | ||||
653 | sub _elem | ||||
654 | { | ||||
655 | my $self = shift; | ||||
656 | my $elem = shift; | ||||
657 | my $old = $self->{$elem}; | ||||
658 | $self->{$elem} = $_[0] if @_; | ||||
659 | return $old; | ||||
660 | } | ||||
661 | |||||
662 | |||||
663 | # Create private _parts attribute from current _content | ||||
664 | sub _parts { | ||||
665 | my $self = shift; | ||||
666 | my $ct = $self->content_type; | ||||
667 | if ($ct =~ m,^multipart/,) { | ||||
668 | require HTTP::Headers::Util; | ||||
669 | my @h = HTTP::Headers::Util::split_header_words($self->header("Content-Type")); | ||||
670 | die "Assert" unless @h; | ||||
671 | my %h = @{$h[0]}; | ||||
672 | if (defined(my $b = $h{boundary})) { | ||||
673 | my $str = $self->content; | ||||
674 | $str =~ s/\r?\n--\Q$b\E--\r?\n.*//s; | ||||
675 | if ($str =~ s/(^|.*?\r?\n)--\Q$b\E\r?\n//s) { | ||||
676 | $self->{_parts} = [map HTTP::Message->parse($_), | ||||
677 | split(/\r?\n--\Q$b\E\r?\n/, $str)] | ||||
678 | } | ||||
679 | } | ||||
680 | } | ||||
681 | elsif ($ct eq "message/http") { | ||||
682 | require HTTP::Request; | ||||
683 | require HTTP::Response; | ||||
684 | my $content = $self->content; | ||||
685 | my $class = ($content =~ m,^(HTTP/.*)\n,) ? | ||||
686 | "HTTP::Response" : "HTTP::Request"; | ||||
687 | $self->{_parts} = [$class->parse($content)]; | ||||
688 | } | ||||
689 | elsif ($ct =~ m,^message/,) { | ||||
690 | $self->{_parts} = [ HTTP::Message->parse($self->content) ]; | ||||
691 | } | ||||
692 | |||||
693 | $self->{_parts} ||= []; | ||||
694 | } | ||||
695 | |||||
696 | |||||
697 | # Create private _content attribute from current _parts | ||||
698 | sub _content { | ||||
699 | my $self = shift; | ||||
700 | my $ct = $self->{_headers}->header("Content-Type") || "multipart/mixed"; | ||||
701 | if ($ct =~ m,^\s*message/,i) { | ||||
702 | _set_content($self, $self->{_parts}[0]->as_string($CRLF), 1); | ||||
703 | return; | ||||
704 | } | ||||
705 | |||||
706 | require HTTP::Headers::Util; | ||||
707 | my @v = HTTP::Headers::Util::split_header_words($ct); | ||||
708 | Carp::carp("Multiple Content-Type headers") if @v > 1; | ||||
709 | @v = @{$v[0]}; | ||||
710 | |||||
711 | my $boundary; | ||||
712 | my $boundary_index; | ||||
713 | for (my @tmp = @v; @tmp;) { | ||||
714 | my($k, $v) = splice(@tmp, 0, 2); | ||||
715 | if ($k eq "boundary") { | ||||
716 | $boundary = $v; | ||||
717 | $boundary_index = @v - @tmp - 1; | ||||
718 | last; | ||||
719 | } | ||||
720 | } | ||||
721 | |||||
722 | my @parts = map $_->as_string($CRLF), @{$self->{_parts}}; | ||||
723 | |||||
724 | my $bno = 0; | ||||
725 | $boundary = _boundary() unless defined $boundary; | ||||
726 | CHECK_BOUNDARY: | ||||
727 | { | ||||
728 | for (@parts) { | ||||
729 | if (index($_, $boundary) >= 0) { | ||||
730 | # must have a better boundary | ||||
731 | $boundary = _boundary(++$bno); | ||||
732 | redo CHECK_BOUNDARY; | ||||
733 | } | ||||
734 | } | ||||
735 | } | ||||
736 | |||||
737 | if ($boundary_index) { | ||||
738 | $v[$boundary_index] = $boundary; | ||||
739 | } | ||||
740 | else { | ||||
741 | push(@v, boundary => $boundary); | ||||
742 | } | ||||
743 | |||||
744 | $ct = HTTP::Headers::Util::join_header_words(@v); | ||||
745 | $self->{_headers}->header("Content-Type", $ct); | ||||
746 | |||||
747 | _set_content($self, "--$boundary$CRLF" . | ||||
748 | join("$CRLF--$boundary$CRLF", @parts) . | ||||
749 | "$CRLF--$boundary--$CRLF", | ||||
750 | 1); | ||||
751 | } | ||||
752 | |||||
753 | |||||
754 | sub _boundary | ||||
755 | { | ||||
756 | my $size = shift || return "xYzZY"; | ||||
757 | require MIME::Base64; | ||||
758 | my $b = MIME::Base64::encode(join("", map chr(rand(256)), 1..$size*3), ""); | ||||
759 | $b =~ s/[\W]/X/g; # ensure alnum only | ||||
760 | $b; | ||||
761 | } | ||||
762 | |||||
763 | |||||
764 | 1 | 12µs | 1; | ||
765 | |||||
766 | |||||
767 | __END__ |