2504
2502
return ($scheme, $host, $port, $path_query);
2508
HTTPMicro::Handle; # hide from PAUSE/indexers
2513
use Errno qw[EINTR EPIPE];
2514
use IO::Socket qw[SOCK_STREAM];
2516
sub BUFSIZE () { 32768 }
2518
my $Printable = sub {
2523
s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
2528
my ($class, %args) = @_;
2532
max_line_size => 16384,
2537
my $ssl_verify_args = {
2538
check_cn => "when_only",
2539
wildcards_in_alt => "anywhere",
2540
wildcards_in_cn => "anywhere"
2544
@_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/);
2545
my ($self, $scheme, $host, $port) = @_;
2547
if ( $scheme eq 'https' ) {
2548
eval "require IO::Socket::SSL"
2549
unless exists $INC{'IO/Socket/SSL.pm'};
2550
croak(qq/IO::Socket::SSL must be installed for https support\n/)
2551
unless $INC{'IO/Socket/SSL.pm'};
2553
elsif ( $scheme ne 'http' ) {
2554
croak(qq/Unsupported URL scheme '$scheme'\n/);
2557
$self->{fh} = 'IO::Socket::INET'->new(
2561
Type => SOCK_STREAM,
2562
Timeout => $self->{timeout}
2563
) or croak(qq/Could not connect to '$host:$port': $@/);
2565
binmode($self->{fh})
2566
or croak(qq/Could not binmode() socket: '$!'/);
2568
if ( $scheme eq 'https') {
2569
IO::Socket::SSL->start_SSL($self->{fh});
2570
ref($self->{fh}) eq 'IO::Socket::SSL'
2571
or die(qq/SSL connection failed for $host\n/);
2572
if ( $self->{fh}->can("verify_hostname") ) {
2573
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
2576
my $fh = $self->{fh};
2577
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
2578
or die(qq/SSL certificate not valid for $host\n/);
2582
$self->{host} = $host;
2583
$self->{port} = $port;
2589
@_ == 1 || croak(q/Usage: $handle->close()/);
2591
CORE::close($self->{fh})
2592
or croak(qq/Could not close socket: '$!'/);
2596
@_ == 2 || croak(q/Usage: $handle->write(buf)/);
2597
my ($self, $buf) = @_;
2599
my $len = length $buf;
2602
local $SIG{PIPE} = 'IGNORE';
2606
or croak(q/Timed out while waiting for socket to become ready for writing/);
2607
my $r = syswrite($self->{fh}, $buf, $len, $off);
2611
last unless $len > 0;
2613
elsif ($! == EPIPE) {
2614
croak(qq/Socket closed by remote server: $!/);
2616
elsif ($! != EINTR) {
2617
croak(qq/Could not write to socket: '$!'/);
2624
@_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len)/);
2625
my ($self, $len) = @_;
2628
my $got = length $self->{rbuf};
2631
my $take = ($got < $len) ? $got : $len;
2632
$buf = substr($self->{rbuf}, 0, $take, '');
2638
or croak(q/Timed out while waiting for socket to become ready for reading/);
2639
my $r = sysread($self->{fh}, $buf, $len, length $buf);
2644
elsif ($! != EINTR) {
2645
croak(qq/Could not read from socket: '$!'/);
2649
croak(q/Unexpected end of stream/);
2655
@_ == 1 || croak(q/Usage: $handle->readline()/);
2659
if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
2663
or croak(q/Timed out while waiting for socket to become ready for reading/);
2664
my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
2668
elsif ($! != EINTR) {
2669
croak(qq/Could not read from socket: '$!'/);
2672
croak(q/Unexpected end of stream while looking for line/);
2675
sub read_header_lines {
2676
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/);
2677
my ($self, $headers) = @_;
2683
my $line = $self->readline;
2685
if ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
2686
my ($field_name) = lc $1;
2687
$val = \($headers->{$field_name} = $2);
2689
elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
2691
or croak(q/Unexpected header continuation line/);
2692
next unless length $1;
2693
$$val .= ' ' if length $$val;
2696
elsif ($line =~ /\A \x0D?\x0A \z/x) {
2700
croak(q/Malformed header line: / . $Printable->($line));
2706
sub write_header_lines {
2707
(@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/);
2708
my($self, $headers) = @_;
2711
while (my ($k, $v) = each %$headers) {
2712
my $field_name = lc $k;
2713
$field_name =~ /\A [\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+ \z/x
2714
or croak(q/Invalid HTTP header field name: / . $Printable->($field_name));
2715
$field_name =~ s/\b(\w)/\u$1/g;
2716
$buf .= "$field_name: $v\x0D\x0A";
2719
return $self->write($buf);
2722
sub read_content_body {
2723
@_ == 3 || @_ == 4 || croak(q/Usage: $handle->read_content_body(callback, response, [read_length])/);
2724
my ($self, $cb, $response, $len) = @_;
2725
$len ||= $response->{headers}{'content-length'};
2727
croak("No content-length in the returned response, and this "
2728
. "UA doesn't implement chunking") unless defined $len;
2731
my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
2732
$cb->($self->read($read), $response);
2739
sub write_content_body {
2740
@_ == 2 || croak(q/Usage: $handle->write_content_body(request)/);
2741
my ($self, $request) = @_;
2742
my ($len, $content_length) = (0, $request->{headers}{'content-length'});
2744
$len += $self->write($request->{content});
2746
$len == $content_length
2747
or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/);
2752
sub read_response_header {
2753
@_ == 1 || croak(q/Usage: $handle->read_response_header()/);
2756
my $line = $self->readline;
2758
$line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
2759
or croak(q/Malformed Status-Line: / . $Printable->($line));
2761
my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
2766
headers => $self->read_header_lines,
2767
protocol => $protocol,
2771
sub write_request_header {
2772
@_ == 4 || croak(q/Usage: $handle->write_request_header(method, request_uri, headers)/);
2773
my ($self, $method, $request_uri, $headers) = @_;
2775
return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
2776
+ $self->write_header_lines($headers);
2780
my ($self, $type, $timeout) = @_;
2781
$timeout = $self->{timeout}
2782
unless defined $timeout && $timeout >= 0;
2784
my $fd = fileno $self->{fh};
2785
defined $fd && $fd >= 0
2786
or croak(q/select(2): 'Bad file descriptor'/);
2789
my $pending = $timeout;
2792
vec(my $fdset = '', $fd, 1) = 1;
2795
$nfound = ($type eq 'read')
2796
? select($fdset, undef, undef, $pending)
2797
: select(undef, $fdset, undef, $pending) ;
2798
if ($nfound == -1) {
2800
or croak(qq/select(2): '$!'/);
2801
redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
2811
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/);
2813
return $self->_do_timeout('read', @_)
2817
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/);
2819
return $self->_do_timeout('write', @_)
2508
package HTTP::Micro::Handle;
2511
use warnings FATAL => 'all';
2512
use English qw(-no_match_vars);
2515
use Errno qw(EINTR EPIPE);
2516
use IO::Socket qw(SOCK_STREAM);
2518
sub BUFSIZE () { 32768 }
2520
my $Printable = sub {
2525
s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
2530
my ($class, %args) = @_;
2534
max_line_size => 16384,
2539
my $ssl_verify_args = {
2540
check_cn => "when_only",
2541
wildcards_in_alt => "anywhere",
2542
wildcards_in_cn => "anywhere"
2546
@_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/);
2547
my ($self, $scheme, $host, $port) = @_;
2549
if ( $scheme eq 'https' ) {
2550
eval "require IO::Socket::SSL"
2551
unless exists $INC{'IO/Socket/SSL.pm'};
2552
croak(qq/IO::Socket::SSL must be installed for https support\n/)
2553
unless $INC{'IO/Socket/SSL.pm'};
2555
elsif ( $scheme ne 'http' ) {
2556
croak(qq/Unsupported URL scheme '$scheme'\n/);
2559
$self->{fh} = IO::Socket::INET->new(
2563
Type => SOCK_STREAM,
2564
Timeout => $self->{timeout}
2565
) or croak(qq/Could not connect to '$host:$port': $@/);
2567
binmode($self->{fh})
2568
or croak(qq/Could not binmode() socket: '$!'/);
2570
if ( $scheme eq 'https') {
2571
IO::Socket::SSL->start_SSL($self->{fh});
2572
ref($self->{fh}) eq 'IO::Socket::SSL'
2573
or die(qq/SSL connection failed for $host\n/);
2574
if ( $self->{fh}->can("verify_hostname") ) {
2575
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
2578
my $fh = $self->{fh};
2579
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
2580
or die(qq/SSL certificate not valid for $host\n/);
2584
$self->{host} = $host;
2585
$self->{port} = $port;
2591
@_ == 1 || croak(q/Usage: $handle->close()/);
2593
CORE::close($self->{fh})
2594
or croak(qq/Could not close socket: '$!'/);
2598
@_ == 2 || croak(q/Usage: $handle->write(buf)/);
2599
my ($self, $buf) = @_;
2601
my $len = length $buf;
2604
local $SIG{PIPE} = 'IGNORE';
2608
or croak(q/Timed out while waiting for socket to become ready for writing/);
2609
my $r = syswrite($self->{fh}, $buf, $len, $off);
2613
last unless $len > 0;
2615
elsif ($! == EPIPE) {
2616
croak(qq/Socket closed by remote server: $!/);
2618
elsif ($! != EINTR) {
2619
croak(qq/Could not write to socket: '$!'/);
2626
@_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len)/);
2627
my ($self, $len) = @_;
2630
my $got = length $self->{rbuf};
2633
my $take = ($got < $len) ? $got : $len;
2634
$buf = substr($self->{rbuf}, 0, $take, '');
2640
or croak(q/Timed out while waiting for socket to become ready for reading/);
2641
my $r = sysread($self->{fh}, $buf, $len, length $buf);
2646
elsif ($! != EINTR) {
2647
croak(qq/Could not read from socket: '$!'/);
2651
croak(q/Unexpected end of stream/);
2657
@_ == 1 || croak(q/Usage: $handle->readline()/);
2661
if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
2665
or croak(q/Timed out while waiting for socket to become ready for reading/);
2666
my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
2670
elsif ($! != EINTR) {
2671
croak(qq/Could not read from socket: '$!'/);
2674
croak(q/Unexpected end of stream while looking for line/);
2677
sub read_header_lines {
2678
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/);
2679
my ($self, $headers) = @_;
2685
my $line = $self->readline;
2687
if ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
2688
my ($field_name) = lc $1;
2689
$val = \($headers->{$field_name} = $2);
2691
elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
2693
or croak(q/Unexpected header continuation line/);
2694
next unless length $1;
2695
$$val .= ' ' if length $$val;
2698
elsif ($line =~ /\A \x0D?\x0A \z/x) {
2702
croak(q/Malformed header line: / . $Printable->($line));
2708
sub write_header_lines {
2709
(@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/);
2710
my($self, $headers) = @_;
2713
while (my ($k, $v) = each %$headers) {
2714
my $field_name = lc $k;
2715
$field_name =~ /\A [\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+ \z/x
2716
or croak(q/Invalid HTTP header field name: / . $Printable->($field_name));
2717
$field_name =~ s/\b(\w)/\u$1/g;
2718
$buf .= "$field_name: $v\x0D\x0A";
2721
return $self->write($buf);
2724
sub read_content_body {
2725
@_ == 3 || @_ == 4 || croak(q/Usage: $handle->read_content_body(callback, response, [read_length])/);
2726
my ($self, $cb, $response, $len) = @_;
2727
$len ||= $response->{headers}{'content-length'};
2729
croak("No content-length in the returned response, and this "
2730
. "UA doesn't implement chunking") unless defined $len;
2733
my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
2734
$cb->($self->read($read), $response);
2741
sub write_content_body {
2742
@_ == 2 || croak(q/Usage: $handle->write_content_body(request)/);
2743
my ($self, $request) = @_;
2744
my ($len, $content_length) = (0, $request->{headers}{'content-length'});
2746
$len += $self->write($request->{content});
2748
$len == $content_length
2749
or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/);
2754
sub read_response_header {
2755
@_ == 1 || croak(q/Usage: $handle->read_response_header()/);
2758
my $line = $self->readline;
2760
$line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
2761
or croak(q/Malformed Status-Line: / . $Printable->($line));
2763
my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
2768
headers => $self->read_header_lines,
2769
protocol => $protocol,
2773
sub write_request_header {
2774
@_ == 4 || croak(q/Usage: $handle->write_request_header(method, request_uri, headers)/);
2775
my ($self, $method, $request_uri, $headers) = @_;
2777
return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
2778
+ $self->write_header_lines($headers);
2782
my ($self, $type, $timeout) = @_;
2783
$timeout = $self->{timeout}
2784
unless defined $timeout && $timeout >= 0;
2786
my $fd = fileno $self->{fh};
2787
defined $fd && $fd >= 0
2788
or croak(q/select(2): 'Bad file descriptor'/);
2791
my $pending = $timeout;
2794
vec(my $fdset = '', $fd, 1) = 1;
2797
$nfound = ($type eq 'read')
2798
? select($fdset, undef, undef, $pending)
2799
: select(undef, $fdset, undef, $pending) ;
2800
if ($nfound == -1) {
2802
or croak(qq/select(2): '$!'/);
2803
redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
2813
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/);
2815
return $self->_do_timeout('read', @_)
2819
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/);
2821
return $self->_do_timeout('write', @_)
2823
} # HTTP::Micro::Handle
2822
2825
my $prog = <<'EOP';