3029
3027
return ($scheme, $host, $port, $path_query);
3033
HTTPMicro::Handle; # hide from PAUSE/indexers
3038
use Errno qw[EINTR EPIPE];
3039
use IO::Socket qw[SOCK_STREAM];
3041
sub BUFSIZE () { 32768 }
3043
my $Printable = sub {
3048
s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
3053
my ($class, %args) = @_;
3057
max_line_size => 16384,
3062
my $ssl_verify_args = {
3063
check_cn => "when_only",
3064
wildcards_in_alt => "anywhere",
3065
wildcards_in_cn => "anywhere"
3069
@_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/);
3070
my ($self, $scheme, $host, $port) = @_;
3072
if ( $scheme eq 'https' ) {
3073
eval "require IO::Socket::SSL"
3074
unless exists $INC{'IO/Socket/SSL.pm'};
3075
croak(qq/IO::Socket::SSL must be installed for https support\n/)
3076
unless $INC{'IO/Socket/SSL.pm'};
3078
elsif ( $scheme ne 'http' ) {
3079
croak(qq/Unsupported URL scheme '$scheme'\n/);
3082
$self->{fh} = 'IO::Socket::INET'->new(
3086
Type => SOCK_STREAM,
3087
Timeout => $self->{timeout}
3088
) or croak(qq/Could not connect to '$host:$port': $@/);
3090
binmode($self->{fh})
3091
or croak(qq/Could not binmode() socket: '$!'/);
3093
if ( $scheme eq 'https') {
3094
IO::Socket::SSL->start_SSL($self->{fh});
3095
ref($self->{fh}) eq 'IO::Socket::SSL'
3096
or die(qq/SSL connection failed for $host\n/);
3097
if ( $self->{fh}->can("verify_hostname") ) {
3098
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
3101
my $fh = $self->{fh};
3102
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
3103
or die(qq/SSL certificate not valid for $host\n/);
3107
$self->{host} = $host;
3108
$self->{port} = $port;
3114
@_ == 1 || croak(q/Usage: $handle->close()/);
3116
CORE::close($self->{fh})
3117
or croak(qq/Could not close socket: '$!'/);
3121
@_ == 2 || croak(q/Usage: $handle->write(buf)/);
3122
my ($self, $buf) = @_;
3124
my $len = length $buf;
3127
local $SIG{PIPE} = 'IGNORE';
3131
or croak(q/Timed out while waiting for socket to become ready for writing/);
3132
my $r = syswrite($self->{fh}, $buf, $len, $off);
3136
last unless $len > 0;
3138
elsif ($! == EPIPE) {
3139
croak(qq/Socket closed by remote server: $!/);
3141
elsif ($! != EINTR) {
3142
croak(qq/Could not write to socket: '$!'/);
3149
@_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len)/);
3150
my ($self, $len) = @_;
3153
my $got = length $self->{rbuf};
3156
my $take = ($got < $len) ? $got : $len;
3157
$buf = substr($self->{rbuf}, 0, $take, '');
3163
or croak(q/Timed out while waiting for socket to become ready for reading/);
3164
my $r = sysread($self->{fh}, $buf, $len, length $buf);
3169
elsif ($! != EINTR) {
3170
croak(qq/Could not read from socket: '$!'/);
3174
croak(q/Unexpected end of stream/);
3180
@_ == 1 || croak(q/Usage: $handle->readline()/);
3184
if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
3188
or croak(q/Timed out while waiting for socket to become ready for reading/);
3189
my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
3193
elsif ($! != EINTR) {
3194
croak(qq/Could not read from socket: '$!'/);
3197
croak(q/Unexpected end of stream while looking for line/);
3200
sub read_header_lines {
3201
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/);
3202
my ($self, $headers) = @_;
3208
my $line = $self->readline;
3210
if ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
3211
my ($field_name) = lc $1;
3212
$val = \($headers->{$field_name} = $2);
3214
elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
3216
or croak(q/Unexpected header continuation line/);
3217
next unless length $1;
3218
$$val .= ' ' if length $$val;
3221
elsif ($line =~ /\A \x0D?\x0A \z/x) {
3225
croak(q/Malformed header line: / . $Printable->($line));
3231
sub write_header_lines {
3232
(@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/);
3233
my($self, $headers) = @_;
3236
while (my ($k, $v) = each %$headers) {
3237
my $field_name = lc $k;
3238
$field_name =~ /\A [\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+ \z/x
3239
or croak(q/Invalid HTTP header field name: / . $Printable->($field_name));
3240
$field_name =~ s/\b(\w)/\u$1/g;
3241
$buf .= "$field_name: $v\x0D\x0A";
3244
return $self->write($buf);
3247
sub read_content_body {
3248
@_ == 3 || @_ == 4 || croak(q/Usage: $handle->read_content_body(callback, response, [read_length])/);
3249
my ($self, $cb, $response, $len) = @_;
3250
$len ||= $response->{headers}{'content-length'};
3252
croak("No content-length in the returned response, and this "
3253
. "UA doesn't implement chunking") unless defined $len;
3256
my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
3257
$cb->($self->read($read), $response);
3264
sub write_content_body {
3265
@_ == 2 || croak(q/Usage: $handle->write_content_body(request)/);
3266
my ($self, $request) = @_;
3267
my ($len, $content_length) = (0, $request->{headers}{'content-length'});
3269
$len += $self->write($request->{content});
3271
$len == $content_length
3272
or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/);
3277
sub read_response_header {
3278
@_ == 1 || croak(q/Usage: $handle->read_response_header()/);
3281
my $line = $self->readline;
3283
$line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
3284
or croak(q/Malformed Status-Line: / . $Printable->($line));
3286
my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
3291
headers => $self->read_header_lines,
3292
protocol => $protocol,
3296
sub write_request_header {
3297
@_ == 4 || croak(q/Usage: $handle->write_request_header(method, request_uri, headers)/);
3298
my ($self, $method, $request_uri, $headers) = @_;
3300
return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
3301
+ $self->write_header_lines($headers);
3305
my ($self, $type, $timeout) = @_;
3306
$timeout = $self->{timeout}
3307
unless defined $timeout && $timeout >= 0;
3309
my $fd = fileno $self->{fh};
3310
defined $fd && $fd >= 0
3311
or croak(q/select(2): 'Bad file descriptor'/);
3314
my $pending = $timeout;
3317
vec(my $fdset = '', $fd, 1) = 1;
3320
$nfound = ($type eq 'read')
3321
? select($fdset, undef, undef, $pending)
3322
: select(undef, $fdset, undef, $pending) ;
3323
if ($nfound == -1) {
3325
or croak(qq/select(2): '$!'/);
3326
redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
3336
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/);
3338
return $self->_do_timeout('read', @_)
3342
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/);
3344
return $self->_do_timeout('write', @_)
3033
package HTTP::Micro::Handle;
3036
use warnings FATAL => 'all';
3037
use English qw(-no_match_vars);
3040
use Errno qw(EINTR EPIPE);
3041
use IO::Socket qw(SOCK_STREAM);
3043
sub BUFSIZE () { 32768 }
3045
my $Printable = sub {
3050
s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
3055
my ($class, %args) = @_;
3059
max_line_size => 16384,
3064
my $ssl_verify_args = {
3065
check_cn => "when_only",
3066
wildcards_in_alt => "anywhere",
3067
wildcards_in_cn => "anywhere"
3071
@_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/);
3072
my ($self, $scheme, $host, $port) = @_;
3074
if ( $scheme eq 'https' ) {
3075
eval "require IO::Socket::SSL"
3076
unless exists $INC{'IO/Socket/SSL.pm'};
3077
croak(qq/IO::Socket::SSL must be installed for https support\n/)
3078
unless $INC{'IO/Socket/SSL.pm'};
3080
elsif ( $scheme ne 'http' ) {
3081
croak(qq/Unsupported URL scheme '$scheme'\n/);
3084
$self->{fh} = IO::Socket::INET->new(
3088
Type => SOCK_STREAM,
3089
Timeout => $self->{timeout}
3090
) or croak(qq/Could not connect to '$host:$port': $@/);
3092
binmode($self->{fh})
3093
or croak(qq/Could not binmode() socket: '$!'/);
3095
if ( $scheme eq 'https') {
3096
IO::Socket::SSL->start_SSL($self->{fh});
3097
ref($self->{fh}) eq 'IO::Socket::SSL'
3098
or die(qq/SSL connection failed for $host\n/);
3099
if ( $self->{fh}->can("verify_hostname") ) {
3100
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
3103
my $fh = $self->{fh};
3104
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
3105
or die(qq/SSL certificate not valid for $host\n/);
3109
$self->{host} = $host;
3110
$self->{port} = $port;
3116
@_ == 1 || croak(q/Usage: $handle->close()/);
3118
CORE::close($self->{fh})
3119
or croak(qq/Could not close socket: '$!'/);
3123
@_ == 2 || croak(q/Usage: $handle->write(buf)/);
3124
my ($self, $buf) = @_;
3126
my $len = length $buf;
3129
local $SIG{PIPE} = 'IGNORE';
3133
or croak(q/Timed out while waiting for socket to become ready for writing/);
3134
my $r = syswrite($self->{fh}, $buf, $len, $off);
3138
last unless $len > 0;
3140
elsif ($! == EPIPE) {
3141
croak(qq/Socket closed by remote server: $!/);
3143
elsif ($! != EINTR) {
3144
croak(qq/Could not write to socket: '$!'/);
3151
@_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len)/);
3152
my ($self, $len) = @_;
3155
my $got = length $self->{rbuf};
3158
my $take = ($got < $len) ? $got : $len;
3159
$buf = substr($self->{rbuf}, 0, $take, '');
3165
or croak(q/Timed out while waiting for socket to become ready for reading/);
3166
my $r = sysread($self->{fh}, $buf, $len, length $buf);
3171
elsif ($! != EINTR) {
3172
croak(qq/Could not read from socket: '$!'/);
3176
croak(q/Unexpected end of stream/);
3182
@_ == 1 || croak(q/Usage: $handle->readline()/);
3186
if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
3190
or croak(q/Timed out while waiting for socket to become ready for reading/);
3191
my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
3195
elsif ($! != EINTR) {
3196
croak(qq/Could not read from socket: '$!'/);
3199
croak(q/Unexpected end of stream while looking for line/);
3202
sub read_header_lines {
3203
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/);
3204
my ($self, $headers) = @_;
3210
my $line = $self->readline;
3212
if ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
3213
my ($field_name) = lc $1;
3214
$val = \($headers->{$field_name} = $2);
3216
elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
3218
or croak(q/Unexpected header continuation line/);
3219
next unless length $1;
3220
$$val .= ' ' if length $$val;
3223
elsif ($line =~ /\A \x0D?\x0A \z/x) {
3227
croak(q/Malformed header line: / . $Printable->($line));
3233
sub write_header_lines {
3234
(@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/);
3235
my($self, $headers) = @_;
3238
while (my ($k, $v) = each %$headers) {
3239
my $field_name = lc $k;
3240
$field_name =~ /\A [\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+ \z/x
3241
or croak(q/Invalid HTTP header field name: / . $Printable->($field_name));
3242
$field_name =~ s/\b(\w)/\u$1/g;
3243
$buf .= "$field_name: $v\x0D\x0A";
3246
return $self->write($buf);
3249
sub read_content_body {
3250
@_ == 3 || @_ == 4 || croak(q/Usage: $handle->read_content_body(callback, response, [read_length])/);
3251
my ($self, $cb, $response, $len) = @_;
3252
$len ||= $response->{headers}{'content-length'};
3254
croak("No content-length in the returned response, and this "
3255
. "UA doesn't implement chunking") unless defined $len;
3258
my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
3259
$cb->($self->read($read), $response);
3266
sub write_content_body {
3267
@_ == 2 || croak(q/Usage: $handle->write_content_body(request)/);
3268
my ($self, $request) = @_;
3269
my ($len, $content_length) = (0, $request->{headers}{'content-length'});
3271
$len += $self->write($request->{content});
3273
$len == $content_length
3274
or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/);
3279
sub read_response_header {
3280
@_ == 1 || croak(q/Usage: $handle->read_response_header()/);
3283
my $line = $self->readline;
3285
$line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
3286
or croak(q/Malformed Status-Line: / . $Printable->($line));
3288
my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
3293
headers => $self->read_header_lines,
3294
protocol => $protocol,
3298
sub write_request_header {
3299
@_ == 4 || croak(q/Usage: $handle->write_request_header(method, request_uri, headers)/);
3300
my ($self, $method, $request_uri, $headers) = @_;
3302
return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
3303
+ $self->write_header_lines($headers);
3307
my ($self, $type, $timeout) = @_;
3308
$timeout = $self->{timeout}
3309
unless defined $timeout && $timeout >= 0;
3311
my $fd = fileno $self->{fh};
3312
defined $fd && $fd >= 0
3313
or croak(q/select(2): 'Bad file descriptor'/);
3316
my $pending = $timeout;
3319
vec(my $fdset = '', $fd, 1) = 1;
3322
$nfound = ($type eq 'read')
3323
? select($fdset, undef, undef, $pending)
3324
: select(undef, $fdset, undef, $pending) ;
3325
if ($nfound == -1) {
3327
or croak(qq/select(2): '$!'/);
3328
redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
3338
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/);
3340
return $self->_do_timeout('read', @_)
3344
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/);
3346
return $self->_do_timeout('write', @_)
3348
} # HTTP::Micro::Handle
3347
3350
my $prog = <<'EOP';