5152
5150
return ($scheme, $host, $port, $path_query);
5156
HTTPMicro::Handle; # hide from PAUSE/indexers
5161
use Errno qw[EINTR EPIPE];
5162
use IO::Socket qw[SOCK_STREAM];
5164
sub BUFSIZE () { 32768 }
5166
my $Printable = sub {
5171
s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
5176
my ($class, %args) = @_;
5180
max_line_size => 16384,
5185
my $ssl_verify_args = {
5186
check_cn => "when_only",
5187
wildcards_in_alt => "anywhere",
5188
wildcards_in_cn => "anywhere"
5192
@_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/);
5193
my ($self, $scheme, $host, $port) = @_;
5195
if ( $scheme eq 'https' ) {
5196
eval "require IO::Socket::SSL"
5197
unless exists $INC{'IO/Socket/SSL.pm'};
5198
croak(qq/IO::Socket::SSL must be installed for https support\n/)
5199
unless $INC{'IO/Socket/SSL.pm'};
5201
elsif ( $scheme ne 'http' ) {
5202
croak(qq/Unsupported URL scheme '$scheme'\n/);
5205
$self->{fh} = 'IO::Socket::INET'->new(
5209
Type => SOCK_STREAM,
5210
Timeout => $self->{timeout}
5211
) or croak(qq/Could not connect to '$host:$port': $@/);
5213
binmode($self->{fh})
5214
or croak(qq/Could not binmode() socket: '$!'/);
5216
if ( $scheme eq 'https') {
5217
IO::Socket::SSL->start_SSL($self->{fh});
5218
ref($self->{fh}) eq 'IO::Socket::SSL'
5219
or die(qq/SSL connection failed for $host\n/);
5220
if ( $self->{fh}->can("verify_hostname") ) {
5221
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
5224
my $fh = $self->{fh};
5225
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
5226
or die(qq/SSL certificate not valid for $host\n/);
5230
$self->{host} = $host;
5231
$self->{port} = $port;
5237
@_ == 1 || croak(q/Usage: $handle->close()/);
5239
CORE::close($self->{fh})
5240
or croak(qq/Could not close socket: '$!'/);
5244
@_ == 2 || croak(q/Usage: $handle->write(buf)/);
5245
my ($self, $buf) = @_;
5247
my $len = length $buf;
5250
local $SIG{PIPE} = 'IGNORE';
5254
or croak(q/Timed out while waiting for socket to become ready for writing/);
5255
my $r = syswrite($self->{fh}, $buf, $len, $off);
5259
last unless $len > 0;
5261
elsif ($! == EPIPE) {
5262
croak(qq/Socket closed by remote server: $!/);
5264
elsif ($! != EINTR) {
5265
croak(qq/Could not write to socket: '$!'/);
5272
@_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len)/);
5273
my ($self, $len) = @_;
5276
my $got = length $self->{rbuf};
5279
my $take = ($got < $len) ? $got : $len;
5280
$buf = substr($self->{rbuf}, 0, $take, '');
5286
or croak(q/Timed out while waiting for socket to become ready for reading/);
5287
my $r = sysread($self->{fh}, $buf, $len, length $buf);
5292
elsif ($! != EINTR) {
5293
croak(qq/Could not read from socket: '$!'/);
5297
croak(q/Unexpected end of stream/);
5303
@_ == 1 || croak(q/Usage: $handle->readline()/);
5307
if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
5311
or croak(q/Timed out while waiting for socket to become ready for reading/);
5312
my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
5316
elsif ($! != EINTR) {
5317
croak(qq/Could not read from socket: '$!'/);
5320
croak(q/Unexpected end of stream while looking for line/);
5323
sub read_header_lines {
5324
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/);
5325
my ($self, $headers) = @_;
5331
my $line = $self->readline;
5333
if ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
5334
my ($field_name) = lc $1;
5335
$val = \($headers->{$field_name} = $2);
5337
elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
5339
or croak(q/Unexpected header continuation line/);
5340
next unless length $1;
5341
$$val .= ' ' if length $$val;
5344
elsif ($line =~ /\A \x0D?\x0A \z/x) {
5348
croak(q/Malformed header line: / . $Printable->($line));
5354
sub write_header_lines {
5355
(@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/);
5356
my($self, $headers) = @_;
5359
while (my ($k, $v) = each %$headers) {
5360
my $field_name = lc $k;
5361
$field_name =~ /\A [\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+ \z/x
5362
or croak(q/Invalid HTTP header field name: / . $Printable->($field_name));
5363
$field_name =~ s/\b(\w)/\u$1/g;
5364
$buf .= "$field_name: $v\x0D\x0A";
5367
return $self->write($buf);
5370
sub read_content_body {
5371
@_ == 3 || @_ == 4 || croak(q/Usage: $handle->read_content_body(callback, response, [read_length])/);
5372
my ($self, $cb, $response, $len) = @_;
5373
$len ||= $response->{headers}{'content-length'};
5375
croak("No content-length in the returned response, and this "
5376
. "UA doesn't implement chunking") unless defined $len;
5379
my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
5380
$cb->($self->read($read), $response);
5387
sub write_content_body {
5388
@_ == 2 || croak(q/Usage: $handle->write_content_body(request)/);
5389
my ($self, $request) = @_;
5390
my ($len, $content_length) = (0, $request->{headers}{'content-length'});
5392
$len += $self->write($request->{content});
5394
$len == $content_length
5395
or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/);
5400
sub read_response_header {
5401
@_ == 1 || croak(q/Usage: $handle->read_response_header()/);
5404
my $line = $self->readline;
5406
$line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
5407
or croak(q/Malformed Status-Line: / . $Printable->($line));
5409
my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
5414
headers => $self->read_header_lines,
5415
protocol => $protocol,
5419
sub write_request_header {
5420
@_ == 4 || croak(q/Usage: $handle->write_request_header(method, request_uri, headers)/);
5421
my ($self, $method, $request_uri, $headers) = @_;
5423
return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
5424
+ $self->write_header_lines($headers);
5428
my ($self, $type, $timeout) = @_;
5429
$timeout = $self->{timeout}
5430
unless defined $timeout && $timeout >= 0;
5432
my $fd = fileno $self->{fh};
5433
defined $fd && $fd >= 0
5434
or croak(q/select(2): 'Bad file descriptor'/);
5437
my $pending = $timeout;
5440
vec(my $fdset = '', $fd, 1) = 1;
5443
$nfound = ($type eq 'read')
5444
? select($fdset, undef, undef, $pending)
5445
: select(undef, $fdset, undef, $pending) ;
5446
if ($nfound == -1) {
5448
or croak(qq/select(2): '$!'/);
5449
redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
5459
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/);
5461
return $self->_do_timeout('read', @_)
5465
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/);
5467
return $self->_do_timeout('write', @_)
5156
package HTTP::Micro::Handle;
5159
use warnings FATAL => 'all';
5160
use English qw(-no_match_vars);
5163
use Errno qw(EINTR EPIPE);
5164
use IO::Socket qw(SOCK_STREAM);
5166
sub BUFSIZE () { 32768 }
5168
my $Printable = sub {
5173
s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
5178
my ($class, %args) = @_;
5182
max_line_size => 16384,
5187
my $ssl_verify_args = {
5188
check_cn => "when_only",
5189
wildcards_in_alt => "anywhere",
5190
wildcards_in_cn => "anywhere"
5194
@_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/);
5195
my ($self, $scheme, $host, $port) = @_;
5197
if ( $scheme eq 'https' ) {
5198
eval "require IO::Socket::SSL"
5199
unless exists $INC{'IO/Socket/SSL.pm'};
5200
croak(qq/IO::Socket::SSL must be installed for https support\n/)
5201
unless $INC{'IO/Socket/SSL.pm'};
5203
elsif ( $scheme ne 'http' ) {
5204
croak(qq/Unsupported URL scheme '$scheme'\n/);
5207
$self->{fh} = IO::Socket::INET->new(
5211
Type => SOCK_STREAM,
5212
Timeout => $self->{timeout}
5213
) or croak(qq/Could not connect to '$host:$port': $@/);
5215
binmode($self->{fh})
5216
or croak(qq/Could not binmode() socket: '$!'/);
5218
if ( $scheme eq 'https') {
5219
IO::Socket::SSL->start_SSL($self->{fh});
5220
ref($self->{fh}) eq 'IO::Socket::SSL'
5221
or die(qq/SSL connection failed for $host\n/);
5222
if ( $self->{fh}->can("verify_hostname") ) {
5223
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
5226
my $fh = $self->{fh};
5227
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
5228
or die(qq/SSL certificate not valid for $host\n/);
5232
$self->{host} = $host;
5233
$self->{port} = $port;
5239
@_ == 1 || croak(q/Usage: $handle->close()/);
5241
CORE::close($self->{fh})
5242
or croak(qq/Could not close socket: '$!'/);
5246
@_ == 2 || croak(q/Usage: $handle->write(buf)/);
5247
my ($self, $buf) = @_;
5249
my $len = length $buf;
5252
local $SIG{PIPE} = 'IGNORE';
5256
or croak(q/Timed out while waiting for socket to become ready for writing/);
5257
my $r = syswrite($self->{fh}, $buf, $len, $off);
5261
last unless $len > 0;
5263
elsif ($! == EPIPE) {
5264
croak(qq/Socket closed by remote server: $!/);
5266
elsif ($! != EINTR) {
5267
croak(qq/Could not write to socket: '$!'/);
5274
@_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len)/);
5275
my ($self, $len) = @_;
5278
my $got = length $self->{rbuf};
5281
my $take = ($got < $len) ? $got : $len;
5282
$buf = substr($self->{rbuf}, 0, $take, '');
5288
or croak(q/Timed out while waiting for socket to become ready for reading/);
5289
my $r = sysread($self->{fh}, $buf, $len, length $buf);
5294
elsif ($! != EINTR) {
5295
croak(qq/Could not read from socket: '$!'/);
5299
croak(q/Unexpected end of stream/);
5305
@_ == 1 || croak(q/Usage: $handle->readline()/);
5309
if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
5313
or croak(q/Timed out while waiting for socket to become ready for reading/);
5314
my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
5318
elsif ($! != EINTR) {
5319
croak(qq/Could not read from socket: '$!'/);
5322
croak(q/Unexpected end of stream while looking for line/);
5325
sub read_header_lines {
5326
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/);
5327
my ($self, $headers) = @_;
5333
my $line = $self->readline;
5335
if ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
5336
my ($field_name) = lc $1;
5337
$val = \($headers->{$field_name} = $2);
5339
elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
5341
or croak(q/Unexpected header continuation line/);
5342
next unless length $1;
5343
$$val .= ' ' if length $$val;
5346
elsif ($line =~ /\A \x0D?\x0A \z/x) {
5350
croak(q/Malformed header line: / . $Printable->($line));
5356
sub write_header_lines {
5357
(@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/);
5358
my($self, $headers) = @_;
5361
while (my ($k, $v) = each %$headers) {
5362
my $field_name = lc $k;
5363
$field_name =~ /\A [\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+ \z/x
5364
or croak(q/Invalid HTTP header field name: / . $Printable->($field_name));
5365
$field_name =~ s/\b(\w)/\u$1/g;
5366
$buf .= "$field_name: $v\x0D\x0A";
5369
return $self->write($buf);
5372
sub read_content_body {
5373
@_ == 3 || @_ == 4 || croak(q/Usage: $handle->read_content_body(callback, response, [read_length])/);
5374
my ($self, $cb, $response, $len) = @_;
5375
$len ||= $response->{headers}{'content-length'};
5377
croak("No content-length in the returned response, and this "
5378
. "UA doesn't implement chunking") unless defined $len;
5381
my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
5382
$cb->($self->read($read), $response);
5389
sub write_content_body {
5390
@_ == 2 || croak(q/Usage: $handle->write_content_body(request)/);
5391
my ($self, $request) = @_;
5392
my ($len, $content_length) = (0, $request->{headers}{'content-length'});
5394
$len += $self->write($request->{content});
5396
$len == $content_length
5397
or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/);
5402
sub read_response_header {
5403
@_ == 1 || croak(q/Usage: $handle->read_response_header()/);
5406
my $line = $self->readline;
5408
$line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
5409
or croak(q/Malformed Status-Line: / . $Printable->($line));
5411
my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
5416
headers => $self->read_header_lines,
5417
protocol => $protocol,
5421
sub write_request_header {
5422
@_ == 4 || croak(q/Usage: $handle->write_request_header(method, request_uri, headers)/);
5423
my ($self, $method, $request_uri, $headers) = @_;
5425
return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
5426
+ $self->write_header_lines($headers);
5430
my ($self, $type, $timeout) = @_;
5431
$timeout = $self->{timeout}
5432
unless defined $timeout && $timeout >= 0;
5434
my $fd = fileno $self->{fh};
5435
defined $fd && $fd >= 0
5436
or croak(q/select(2): 'Bad file descriptor'/);
5439
my $pending = $timeout;
5442
vec(my $fdset = '', $fd, 1) = 1;
5445
$nfound = ($type eq 'read')
5446
? select($fdset, undef, undef, $pending)
5447
: select(undef, $fdset, undef, $pending) ;
5448
if ($nfound == -1) {
5450
or croak(qq/select(2): '$!'/);
5451
redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
5461
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/);
5463
return $self->_do_timeout('read', @_)
5467
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/);
5469
return $self->_do_timeout('write', @_)
5471
} # HTTP::Micro::Handle
5470
5473
my $prog = <<'EOP';