8538
8536
return ($scheme, $host, $port, $path_query);
8542
HTTPMicro::Handle; # hide from PAUSE/indexers
8547
use Errno qw[EINTR EPIPE];
8548
use IO::Socket qw[SOCK_STREAM];
8550
sub BUFSIZE () { 32768 }
8552
my $Printable = sub {
8557
s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
8562
my ($class, %args) = @_;
8566
max_line_size => 16384,
8571
my $ssl_verify_args = {
8572
check_cn => "when_only",
8573
wildcards_in_alt => "anywhere",
8574
wildcards_in_cn => "anywhere"
8578
@_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/);
8579
my ($self, $scheme, $host, $port) = @_;
8581
if ( $scheme eq 'https' ) {
8582
eval "require IO::Socket::SSL"
8583
unless exists $INC{'IO/Socket/SSL.pm'};
8584
croak(qq/IO::Socket::SSL must be installed for https support\n/)
8585
unless $INC{'IO/Socket/SSL.pm'};
8587
elsif ( $scheme ne 'http' ) {
8588
croak(qq/Unsupported URL scheme '$scheme'\n/);
8591
$self->{fh} = 'IO::Socket::INET'->new(
8595
Type => SOCK_STREAM,
8596
Timeout => $self->{timeout}
8597
) or croak(qq/Could not connect to '$host:$port': $@/);
8599
binmode($self->{fh})
8600
or croak(qq/Could not binmode() socket: '$!'/);
8602
if ( $scheme eq 'https') {
8603
IO::Socket::SSL->start_SSL($self->{fh});
8604
ref($self->{fh}) eq 'IO::Socket::SSL'
8605
or die(qq/SSL connection failed for $host\n/);
8606
if ( $self->{fh}->can("verify_hostname") ) {
8607
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
8610
my $fh = $self->{fh};
8611
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
8612
or die(qq/SSL certificate not valid for $host\n/);
8616
$self->{host} = $host;
8617
$self->{port} = $port;
8623
@_ == 1 || croak(q/Usage: $handle->close()/);
8625
CORE::close($self->{fh})
8626
or croak(qq/Could not close socket: '$!'/);
8630
@_ == 2 || croak(q/Usage: $handle->write(buf)/);
8631
my ($self, $buf) = @_;
8633
my $len = length $buf;
8636
local $SIG{PIPE} = 'IGNORE';
8640
or croak(q/Timed out while waiting for socket to become ready for writing/);
8641
my $r = syswrite($self->{fh}, $buf, $len, $off);
8645
last unless $len > 0;
8647
elsif ($! == EPIPE) {
8648
croak(qq/Socket closed by remote server: $!/);
8650
elsif ($! != EINTR) {
8651
croak(qq/Could not write to socket: '$!'/);
8658
@_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len)/);
8659
my ($self, $len) = @_;
8662
my $got = length $self->{rbuf};
8665
my $take = ($got < $len) ? $got : $len;
8666
$buf = substr($self->{rbuf}, 0, $take, '');
8672
or croak(q/Timed out while waiting for socket to become ready for reading/);
8673
my $r = sysread($self->{fh}, $buf, $len, length $buf);
8678
elsif ($! != EINTR) {
8679
croak(qq/Could not read from socket: '$!'/);
8683
croak(q/Unexpected end of stream/);
8689
@_ == 1 || croak(q/Usage: $handle->readline()/);
8693
if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
8697
or croak(q/Timed out while waiting for socket to become ready for reading/);
8698
my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
8702
elsif ($! != EINTR) {
8703
croak(qq/Could not read from socket: '$!'/);
8706
croak(q/Unexpected end of stream while looking for line/);
8709
sub read_header_lines {
8710
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/);
8711
my ($self, $headers) = @_;
8717
my $line = $self->readline;
8719
if ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
8720
my ($field_name) = lc $1;
8721
$val = \($headers->{$field_name} = $2);
8723
elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
8725
or croak(q/Unexpected header continuation line/);
8726
next unless length $1;
8727
$$val .= ' ' if length $$val;
8730
elsif ($line =~ /\A \x0D?\x0A \z/x) {
8734
croak(q/Malformed header line: / . $Printable->($line));
8740
sub write_header_lines {
8741
(@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/);
8742
my($self, $headers) = @_;
8745
while (my ($k, $v) = each %$headers) {
8746
my $field_name = lc $k;
8747
$field_name =~ /\A [\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+ \z/x
8748
or croak(q/Invalid HTTP header field name: / . $Printable->($field_name));
8749
$field_name =~ s/\b(\w)/\u$1/g;
8750
$buf .= "$field_name: $v\x0D\x0A";
8753
return $self->write($buf);
8756
sub read_content_body {
8757
@_ == 3 || @_ == 4 || croak(q/Usage: $handle->read_content_body(callback, response, [read_length])/);
8758
my ($self, $cb, $response, $len) = @_;
8759
$len ||= $response->{headers}{'content-length'};
8761
croak("No content-length in the returned response, and this "
8762
. "UA doesn't implement chunking") unless defined $len;
8765
my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
8766
$cb->($self->read($read), $response);
8773
sub write_content_body {
8774
@_ == 2 || croak(q/Usage: $handle->write_content_body(request)/);
8775
my ($self, $request) = @_;
8776
my ($len, $content_length) = (0, $request->{headers}{'content-length'});
8778
$len += $self->write($request->{content});
8780
$len == $content_length
8781
or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/);
8786
sub read_response_header {
8787
@_ == 1 || croak(q/Usage: $handle->read_response_header()/);
8790
my $line = $self->readline;
8792
$line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
8793
or croak(q/Malformed Status-Line: / . $Printable->($line));
8795
my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
8800
headers => $self->read_header_lines,
8801
protocol => $protocol,
8805
sub write_request_header {
8806
@_ == 4 || croak(q/Usage: $handle->write_request_header(method, request_uri, headers)/);
8807
my ($self, $method, $request_uri, $headers) = @_;
8809
return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
8810
+ $self->write_header_lines($headers);
8814
my ($self, $type, $timeout) = @_;
8815
$timeout = $self->{timeout}
8816
unless defined $timeout && $timeout >= 0;
8818
my $fd = fileno $self->{fh};
8819
defined $fd && $fd >= 0
8820
or croak(q/select(2): 'Bad file descriptor'/);
8823
my $pending = $timeout;
8826
vec(my $fdset = '', $fd, 1) = 1;
8829
$nfound = ($type eq 'read')
8830
? select($fdset, undef, undef, $pending)
8831
: select(undef, $fdset, undef, $pending) ;
8832
if ($nfound == -1) {
8834
or croak(qq/select(2): '$!'/);
8835
redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
8845
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/);
8847
return $self->_do_timeout('read', @_)
8851
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/);
8853
return $self->_do_timeout('write', @_)
8542
package HTTP::Micro::Handle;
8545
use warnings FATAL => 'all';
8546
use English qw(-no_match_vars);
8549
use Errno qw(EINTR EPIPE);
8550
use IO::Socket qw(SOCK_STREAM);
8552
sub BUFSIZE () { 32768 }
8554
my $Printable = sub {
8559
s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
8564
my ($class, %args) = @_;
8568
max_line_size => 16384,
8573
my $ssl_verify_args = {
8574
check_cn => "when_only",
8575
wildcards_in_alt => "anywhere",
8576
wildcards_in_cn => "anywhere"
8580
@_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/);
8581
my ($self, $scheme, $host, $port) = @_;
8583
if ( $scheme eq 'https' ) {
8584
eval "require IO::Socket::SSL"
8585
unless exists $INC{'IO/Socket/SSL.pm'};
8586
croak(qq/IO::Socket::SSL must be installed for https support\n/)
8587
unless $INC{'IO/Socket/SSL.pm'};
8589
elsif ( $scheme ne 'http' ) {
8590
croak(qq/Unsupported URL scheme '$scheme'\n/);
8593
$self->{fh} = IO::Socket::INET->new(
8597
Type => SOCK_STREAM,
8598
Timeout => $self->{timeout}
8599
) or croak(qq/Could not connect to '$host:$port': $@/);
8601
binmode($self->{fh})
8602
or croak(qq/Could not binmode() socket: '$!'/);
8604
if ( $scheme eq 'https') {
8605
IO::Socket::SSL->start_SSL($self->{fh});
8606
ref($self->{fh}) eq 'IO::Socket::SSL'
8607
or die(qq/SSL connection failed for $host\n/);
8608
if ( $self->{fh}->can("verify_hostname") ) {
8609
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
8612
my $fh = $self->{fh};
8613
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
8614
or die(qq/SSL certificate not valid for $host\n/);
8618
$self->{host} = $host;
8619
$self->{port} = $port;
8625
@_ == 1 || croak(q/Usage: $handle->close()/);
8627
CORE::close($self->{fh})
8628
or croak(qq/Could not close socket: '$!'/);
8632
@_ == 2 || croak(q/Usage: $handle->write(buf)/);
8633
my ($self, $buf) = @_;
8635
my $len = length $buf;
8638
local $SIG{PIPE} = 'IGNORE';
8642
or croak(q/Timed out while waiting for socket to become ready for writing/);
8643
my $r = syswrite($self->{fh}, $buf, $len, $off);
8647
last unless $len > 0;
8649
elsif ($! == EPIPE) {
8650
croak(qq/Socket closed by remote server: $!/);
8652
elsif ($! != EINTR) {
8653
croak(qq/Could not write to socket: '$!'/);
8660
@_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len)/);
8661
my ($self, $len) = @_;
8664
my $got = length $self->{rbuf};
8667
my $take = ($got < $len) ? $got : $len;
8668
$buf = substr($self->{rbuf}, 0, $take, '');
8674
or croak(q/Timed out while waiting for socket to become ready for reading/);
8675
my $r = sysread($self->{fh}, $buf, $len, length $buf);
8680
elsif ($! != EINTR) {
8681
croak(qq/Could not read from socket: '$!'/);
8685
croak(q/Unexpected end of stream/);
8691
@_ == 1 || croak(q/Usage: $handle->readline()/);
8695
if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
8699
or croak(q/Timed out while waiting for socket to become ready for reading/);
8700
my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
8704
elsif ($! != EINTR) {
8705
croak(qq/Could not read from socket: '$!'/);
8708
croak(q/Unexpected end of stream while looking for line/);
8711
sub read_header_lines {
8712
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/);
8713
my ($self, $headers) = @_;
8719
my $line = $self->readline;
8721
if ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
8722
my ($field_name) = lc $1;
8723
$val = \($headers->{$field_name} = $2);
8725
elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
8727
or croak(q/Unexpected header continuation line/);
8728
next unless length $1;
8729
$$val .= ' ' if length $$val;
8732
elsif ($line =~ /\A \x0D?\x0A \z/x) {
8736
croak(q/Malformed header line: / . $Printable->($line));
8742
sub write_header_lines {
8743
(@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/);
8744
my($self, $headers) = @_;
8747
while (my ($k, $v) = each %$headers) {
8748
my $field_name = lc $k;
8749
$field_name =~ /\A [\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+ \z/x
8750
or croak(q/Invalid HTTP header field name: / . $Printable->($field_name));
8751
$field_name =~ s/\b(\w)/\u$1/g;
8752
$buf .= "$field_name: $v\x0D\x0A";
8755
return $self->write($buf);
8758
sub read_content_body {
8759
@_ == 3 || @_ == 4 || croak(q/Usage: $handle->read_content_body(callback, response, [read_length])/);
8760
my ($self, $cb, $response, $len) = @_;
8761
$len ||= $response->{headers}{'content-length'};
8763
croak("No content-length in the returned response, and this "
8764
. "UA doesn't implement chunking") unless defined $len;
8767
my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
8768
$cb->($self->read($read), $response);
8775
sub write_content_body {
8776
@_ == 2 || croak(q/Usage: $handle->write_content_body(request)/);
8777
my ($self, $request) = @_;
8778
my ($len, $content_length) = (0, $request->{headers}{'content-length'});
8780
$len += $self->write($request->{content});
8782
$len == $content_length
8783
or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/);
8788
sub read_response_header {
8789
@_ == 1 || croak(q/Usage: $handle->read_response_header()/);
8792
my $line = $self->readline;
8794
$line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
8795
or croak(q/Malformed Status-Line: / . $Printable->($line));
8797
my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
8802
headers => $self->read_header_lines,
8803
protocol => $protocol,
8807
sub write_request_header {
8808
@_ == 4 || croak(q/Usage: $handle->write_request_header(method, request_uri, headers)/);
8809
my ($self, $method, $request_uri, $headers) = @_;
8811
return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
8812
+ $self->write_header_lines($headers);
8816
my ($self, $type, $timeout) = @_;
8817
$timeout = $self->{timeout}
8818
unless defined $timeout && $timeout >= 0;
8820
my $fd = fileno $self->{fh};
8821
defined $fd && $fd >= 0
8822
or croak(q/select(2): 'Bad file descriptor'/);
8825
my $pending = $timeout;
8828
vec(my $fdset = '', $fd, 1) = 1;
8831
$nfound = ($type eq 'read')
8832
? select($fdset, undef, undef, $pending)
8833
: select(undef, $fdset, undef, $pending) ;
8834
if ($nfound == -1) {
8836
or croak(qq/select(2): '$!'/);
8837
redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
8847
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/);
8849
return $self->_do_timeout('read', @_)
8853
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/);
8855
return $self->_do_timeout('write', @_)
8857
} # HTTP::Micro::Handle
8856
8859
my $prog = <<'EOP';