169
167
return ($scheme, $host, $port, $path_query);
173
HTTPMicro::Handle; # hide from PAUSE/indexers
178
use Errno qw[EINTR EPIPE];
179
use IO::Socket qw[SOCK_STREAM];
181
sub BUFSIZE () { 32768 }
183
my $Printable = sub {
188
s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
193
my ($class, %args) = @_;
197
max_line_size => 16384,
202
my $ssl_verify_args = {
203
check_cn => "when_only",
204
wildcards_in_alt => "anywhere",
205
wildcards_in_cn => "anywhere"
209
@_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/);
210
my ($self, $scheme, $host, $port) = @_;
212
if ( $scheme eq 'https' ) {
213
eval "require IO::Socket::SSL"
214
unless exists $INC{'IO/Socket/SSL.pm'};
215
croak(qq/IO::Socket::SSL must be installed for https support\n/)
216
unless $INC{'IO/Socket/SSL.pm'};
218
elsif ( $scheme ne 'http' ) {
219
croak(qq/Unsupported URL scheme '$scheme'\n/);
222
$self->{fh} = 'IO::Socket::INET'->new(
227
Timeout => $self->{timeout}
228
) or croak(qq/Could not connect to '$host:$port': $@/);
231
or croak(qq/Could not binmode() socket: '$!'/);
233
if ( $scheme eq 'https') {
234
IO::Socket::SSL->start_SSL($self->{fh});
235
ref($self->{fh}) eq 'IO::Socket::SSL'
236
or die(qq/SSL connection failed for $host\n/);
237
if ( $self->{fh}->can("verify_hostname") ) {
238
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
241
# Can't use $self->{fh}->verify_hostname because the IO::Socket::SSL
242
# that comes from yum doesn't have it, so use our inlined version.
243
my $fh = $self->{fh};
244
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
245
or die(qq/SSL certificate not valid for $host\n/);
249
$self->{host} = $host;
250
$self->{port} = $port;
256
@_ == 1 || croak(q/Usage: $handle->close()/);
258
CORE::close($self->{fh})
259
or croak(qq/Could not close socket: '$!'/);
263
@_ == 2 || croak(q/Usage: $handle->write(buf)/);
264
my ($self, $buf) = @_;
266
my $len = length $buf;
269
local $SIG{PIPE} = 'IGNORE';
273
or croak(q/Timed out while waiting for socket to become ready for writing/);
274
my $r = syswrite($self->{fh}, $buf, $len, $off);
278
last unless $len > 0;
280
elsif ($! == EPIPE) {
281
croak(qq/Socket closed by remote server: $!/);
283
elsif ($! != EINTR) {
284
croak(qq/Could not write to socket: '$!'/);
291
@_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len)/);
292
my ($self, $len) = @_;
295
my $got = length $self->{rbuf};
298
my $take = ($got < $len) ? $got : $len;
299
$buf = substr($self->{rbuf}, 0, $take, '');
305
or croak(q/Timed out while waiting for socket to become ready for reading/);
306
my $r = sysread($self->{fh}, $buf, $len, length $buf);
311
elsif ($! != EINTR) {
312
croak(qq/Could not read from socket: '$!'/);
316
croak(q/Unexpected end of stream/);
322
@_ == 1 || croak(q/Usage: $handle->readline()/);
326
if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
330
or croak(q/Timed out while waiting for socket to become ready for reading/);
331
my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
335
elsif ($! != EINTR) {
336
croak(qq/Could not read from socket: '$!'/);
339
croak(q/Unexpected end of stream while looking for line/);
342
sub read_header_lines {
343
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/);
344
my ($self, $headers) = @_;
350
my $line = $self->readline;
352
if ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
353
my ($field_name) = lc $1;
354
$val = \($headers->{$field_name} = $2);
356
elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
358
or croak(q/Unexpected header continuation line/);
359
next unless length $1;
360
$$val .= ' ' if length $$val;
363
elsif ($line =~ /\A \x0D?\x0A \z/x) {
367
croak(q/Malformed header line: / . $Printable->($line));
373
sub write_header_lines {
374
(@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/);
375
my($self, $headers) = @_;
378
while (my ($k, $v) = each %$headers) {
379
my $field_name = lc $k;
380
$field_name =~ /\A [\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+ \z/x
381
or croak(q/Invalid HTTP header field name: / . $Printable->($field_name));
382
$field_name =~ s/\b(\w)/\u$1/g;
383
$buf .= "$field_name: $v\x0D\x0A";
386
return $self->write($buf);
389
sub read_content_body {
390
@_ == 3 || @_ == 4 || croak(q/Usage: $handle->read_content_body(callback, response, [read_length])/);
391
my ($self, $cb, $response, $len) = @_;
392
$len ||= $response->{headers}{'content-length'};
394
croak("No content-length in the returned response, and this "
395
. "UA doesn't implement chunking") unless defined $len;
398
my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
399
$cb->($self->read($read), $response);
406
sub write_content_body {
407
@_ == 2 || croak(q/Usage: $handle->write_content_body(request)/);
408
my ($self, $request) = @_;
409
my ($len, $content_length) = (0, $request->{headers}{'content-length'});
411
$len += $self->write($request->{content});
413
$len == $content_length
414
or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/);
419
sub read_response_header {
420
@_ == 1 || croak(q/Usage: $handle->read_response_header()/);
423
my $line = $self->readline;
425
$line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
426
or croak(q/Malformed Status-Line: / . $Printable->($line));
428
my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
433
headers => $self->read_header_lines,
434
protocol => $protocol,
438
sub write_request_header {
439
@_ == 4 || croak(q/Usage: $handle->write_request_header(method, request_uri, headers)/);
440
my ($self, $method, $request_uri, $headers) = @_;
442
return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
443
+ $self->write_header_lines($headers);
447
my ($self, $type, $timeout) = @_;
448
$timeout = $self->{timeout}
449
unless defined $timeout && $timeout >= 0;
451
my $fd = fileno $self->{fh};
452
defined $fd && $fd >= 0
453
or croak(q/select(2): 'Bad file descriptor'/);
456
my $pending = $timeout;
459
vec(my $fdset = '', $fd, 1) = 1;
462
$nfound = ($type eq 'read')
463
? select($fdset, undef, undef, $pending)
464
: select(undef, $fdset, undef, $pending) ;
467
or croak(qq/select(2): '$!'/);
468
redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
478
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/);
480
return $self->_do_timeout('read', @_)
484
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/);
486
return $self->_do_timeout('write', @_)
173
package HTTP::Micro::Handle;
176
use warnings FATAL => 'all';
177
use English qw(-no_match_vars);
180
use Errno qw(EINTR EPIPE);
181
use IO::Socket qw(SOCK_STREAM);
183
sub BUFSIZE () { 32768 }
185
my $Printable = sub {
190
s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
195
my ($class, %args) = @_;
199
max_line_size => 16384,
204
my $ssl_verify_args = {
205
check_cn => "when_only",
206
wildcards_in_alt => "anywhere",
207
wildcards_in_cn => "anywhere"
211
@_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/);
212
my ($self, $scheme, $host, $port) = @_;
214
if ( $scheme eq 'https' ) {
215
eval "require IO::Socket::SSL"
216
unless exists $INC{'IO/Socket/SSL.pm'};
217
croak(qq/IO::Socket::SSL must be installed for https support\n/)
218
unless $INC{'IO/Socket/SSL.pm'};
220
elsif ( $scheme ne 'http' ) {
221
croak(qq/Unsupported URL scheme '$scheme'\n/);
224
$self->{fh} = IO::Socket::INET->new(
229
Timeout => $self->{timeout}
230
) or croak(qq/Could not connect to '$host:$port': $@/);
233
or croak(qq/Could not binmode() socket: '$!'/);
235
if ( $scheme eq 'https') {
236
IO::Socket::SSL->start_SSL($self->{fh});
237
ref($self->{fh}) eq 'IO::Socket::SSL'
238
or die(qq/SSL connection failed for $host\n/);
239
if ( $self->{fh}->can("verify_hostname") ) {
240
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
243
# Can't use $self->{fh}->verify_hostname because the IO::Socket::SSL
244
# that comes from yum doesn't have it, so use our inlined version.
245
my $fh = $self->{fh};
246
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
247
or die(qq/SSL certificate not valid for $host\n/);
251
$self->{host} = $host;
252
$self->{port} = $port;
258
@_ == 1 || croak(q/Usage: $handle->close()/);
260
CORE::close($self->{fh})
261
or croak(qq/Could not close socket: '$!'/);
265
@_ == 2 || croak(q/Usage: $handle->write(buf)/);
266
my ($self, $buf) = @_;
268
my $len = length $buf;
271
local $SIG{PIPE} = 'IGNORE';
275
or croak(q/Timed out while waiting for socket to become ready for writing/);
276
my $r = syswrite($self->{fh}, $buf, $len, $off);
280
last unless $len > 0;
282
elsif ($! == EPIPE) {
283
croak(qq/Socket closed by remote server: $!/);
285
elsif ($! != EINTR) {
286
croak(qq/Could not write to socket: '$!'/);
293
@_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len)/);
294
my ($self, $len) = @_;
297
my $got = length $self->{rbuf};
300
my $take = ($got < $len) ? $got : $len;
301
$buf = substr($self->{rbuf}, 0, $take, '');
307
or croak(q/Timed out while waiting for socket to become ready for reading/);
308
my $r = sysread($self->{fh}, $buf, $len, length $buf);
313
elsif ($! != EINTR) {
314
croak(qq/Could not read from socket: '$!'/);
318
croak(q/Unexpected end of stream/);
324
@_ == 1 || croak(q/Usage: $handle->readline()/);
328
if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
332
or croak(q/Timed out while waiting for socket to become ready for reading/);
333
my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
337
elsif ($! != EINTR) {
338
croak(qq/Could not read from socket: '$!'/);
341
croak(q/Unexpected end of stream while looking for line/);
344
sub read_header_lines {
345
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/);
346
my ($self, $headers) = @_;
352
my $line = $self->readline;
354
if ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
355
my ($field_name) = lc $1;
356
$val = \($headers->{$field_name} = $2);
358
elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
360
or croak(q/Unexpected header continuation line/);
361
next unless length $1;
362
$$val .= ' ' if length $$val;
365
elsif ($line =~ /\A \x0D?\x0A \z/x) {
369
croak(q/Malformed header line: / . $Printable->($line));
375
sub write_header_lines {
376
(@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/);
377
my($self, $headers) = @_;
380
while (my ($k, $v) = each %$headers) {
381
my $field_name = lc $k;
382
$field_name =~ /\A [\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+ \z/x
383
or croak(q/Invalid HTTP header field name: / . $Printable->($field_name));
384
$field_name =~ s/\b(\w)/\u$1/g;
385
$buf .= "$field_name: $v\x0D\x0A";
388
return $self->write($buf);
391
sub read_content_body {
392
@_ == 3 || @_ == 4 || croak(q/Usage: $handle->read_content_body(callback, response, [read_length])/);
393
my ($self, $cb, $response, $len) = @_;
394
$len ||= $response->{headers}{'content-length'};
396
croak("No content-length in the returned response, and this "
397
. "UA doesn't implement chunking") unless defined $len;
400
my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
401
$cb->($self->read($read), $response);
408
sub write_content_body {
409
@_ == 2 || croak(q/Usage: $handle->write_content_body(request)/);
410
my ($self, $request) = @_;
411
my ($len, $content_length) = (0, $request->{headers}{'content-length'});
413
$len += $self->write($request->{content});
415
$len == $content_length
416
or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/);
421
sub read_response_header {
422
@_ == 1 || croak(q/Usage: $handle->read_response_header()/);
425
my $line = $self->readline;
427
$line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
428
or croak(q/Malformed Status-Line: / . $Printable->($line));
430
my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
435
headers => $self->read_header_lines,
436
protocol => $protocol,
440
sub write_request_header {
441
@_ == 4 || croak(q/Usage: $handle->write_request_header(method, request_uri, headers)/);
442
my ($self, $method, $request_uri, $headers) = @_;
444
return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
445
+ $self->write_header_lines($headers);
449
my ($self, $type, $timeout) = @_;
450
$timeout = $self->{timeout}
451
unless defined $timeout && $timeout >= 0;
453
my $fd = fileno $self->{fh};
454
defined $fd && $fd >= 0
455
or croak(q/select(2): 'Bad file descriptor'/);
458
my $pending = $timeout;
461
vec(my $fdset = '', $fd, 1) = 1;
464
$nfound = ($type eq 'read')
465
? select($fdset, undef, undef, $pending)
466
: select(undef, $fdset, undef, $pending) ;
469
or croak(qq/select(2): '$!'/);
470
redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
480
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/);
482
return $self->_do_timeout('read', @_)
486
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/);
488
return $self->_do_timeout('write', @_)
490
} # HTTP::Micro::Handle
489
492
# Partially copy-pasted from IO::Socket::SSL 1.76, with some changes because
490
493
# we're forced to use IO::Socket::SSL version 1.01 in yum-based distros