~percona-toolkit-dev/percona-toolkit/pt-stalk-sleep-collect-option

« back to all changes in this revision

Viewing changes to lib/HTTP/Micro.pm

  • Committer: Daniel Nichter
  • Date: 2013-06-19 21:23:55 UTC
  • mfrom: (582.1.5 release-2.2.3)
  • Revision ID: daniel@percona.com-20130619212355-nf6bmx23j3b76afe
Tags: 2.2.3
Merge release-2.2.3.

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16
16
# Place, Suite 330, Boston, MA  02111-1307  USA.
17
17
# ###########################################################################
18
 
# HTTPMicro package
 
18
# HTTP::Micro package
19
19
# ###########################################################################
20
20
{
21
 
# Package: HTTPMicro
 
21
# Package: HTTP::Micro
22
22
# A stripped down version of HTTP::Tiny; but not a correct HTTP/1.1
23
 
# implementation
24
 
 
25
 
package HTTPMicro;
26
 
BEGIN {
27
 
  $HTTPMicro::VERSION = '0.001';
28
 
}
 
23
# implementation.
 
24
package HTTP::Micro;
 
25
 
 
26
our $VERSION = '0.01';
 
27
 
29
28
use strict;
30
 
use warnings;
31
 
 
 
29
use warnings FATAL => 'all';
 
30
use English qw(-no_match_vars);
32
31
use Carp ();
33
32
 
34
 
 
35
33
my @attributes;
36
34
BEGIN {
37
35
    @attributes = qw(agent timeout);
103
101
        headers   => {},
104
102
    };
105
103
 
106
 
    my $handle  = HTTPMicro::Handle->new(timeout => $self->{timeout});
 
104
    my $handle  = HTTP::Micro::Handle->new(timeout => $self->{timeout});
107
105
 
108
106
    $handle->connect($scheme, $host, $port);
109
107
 
169
167
    return ($scheme, $host, $port, $path_query);
170
168
}
171
169
 
172
 
package
173
 
    HTTPMicro::Handle; # hide from PAUSE/indexers
174
 
use strict;
175
 
use warnings;
176
 
 
177
 
use Carp       qw[croak];
178
 
use Errno      qw[EINTR EPIPE];
179
 
use IO::Socket qw[SOCK_STREAM];
180
 
 
181
 
sub BUFSIZE () { 32768 }
182
 
 
183
 
my $Printable = sub {
184
 
    local $_ = shift;
185
 
    s/\r/\\r/g;
186
 
    s/\n/\\n/g;
187
 
    s/\t/\\t/g;
188
 
    s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
189
 
    $_;
190
 
};
191
 
 
192
 
sub new {
193
 
    my ($class, %args) = @_;
194
 
    return bless {
195
 
        rbuf             => '',
196
 
        timeout          => 60,
197
 
        max_line_size    => 16384,
198
 
        %args
199
 
    }, $class;
200
 
}
201
 
 
202
 
my $ssl_verify_args = {
203
 
    check_cn => "when_only",
204
 
    wildcards_in_alt => "anywhere",
205
 
    wildcards_in_cn => "anywhere"
206
 
};
207
 
 
208
 
sub connect {
209
 
    @_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/);
210
 
    my ($self, $scheme, $host, $port) = @_;
211
 
 
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'};
217
 
    }
218
 
    elsif ( $scheme ne 'http' ) {
219
 
      croak(qq/Unsupported URL scheme '$scheme'\n/);
220
 
    }
221
 
 
222
 
    $self->{fh} = 'IO::Socket::INET'->new(
223
 
        PeerHost  => $host,
224
 
        PeerPort  => $port,
225
 
        Proto     => 'tcp',
226
 
        Type      => SOCK_STREAM,
227
 
        Timeout   => $self->{timeout}
228
 
    ) or croak(qq/Could not connect to '$host:$port': $@/);
229
 
 
230
 
    binmode($self->{fh})
231
 
      or croak(qq/Could not binmode() socket: '$!'/);
232
 
 
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 );
239
 
        }
240
 
        else {
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/);
246
 
         }
247
 
    }
248
 
      
249
 
    $self->{host} = $host;
250
 
    $self->{port} = $port;
251
 
 
252
 
    return $self;
253
 
}
254
 
 
255
 
sub close {
256
 
    @_ == 1 || croak(q/Usage: $handle->close()/);
257
 
    my ($self) = @_;
258
 
    CORE::close($self->{fh})
259
 
      or croak(qq/Could not close socket: '$!'/);
260
 
}
261
 
 
262
 
sub write {
263
 
    @_ == 2 || croak(q/Usage: $handle->write(buf)/);
264
 
    my ($self, $buf) = @_;
265
 
 
266
 
    my $len = length $buf;
267
 
    my $off = 0;
268
 
 
269
 
    local $SIG{PIPE} = 'IGNORE';
270
 
 
271
 
    while () {
272
 
        $self->can_write
273
 
          or croak(q/Timed out while waiting for socket to become ready for writing/);
274
 
        my $r = syswrite($self->{fh}, $buf, $len, $off);
275
 
        if (defined $r) {
276
 
            $len -= $r;
277
 
            $off += $r;
278
 
            last unless $len > 0;
279
 
        }
280
 
        elsif ($! == EPIPE) {
281
 
            croak(qq/Socket closed by remote server: $!/);
282
 
        }
283
 
        elsif ($! != EINTR) {
284
 
            croak(qq/Could not write to socket: '$!'/);
285
 
        }
286
 
    }
287
 
    return $off;
288
 
}
289
 
 
290
 
sub read {
291
 
    @_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len)/);
292
 
    my ($self, $len) = @_;
293
 
 
294
 
    my $buf  = '';
295
 
    my $got = length $self->{rbuf};
296
 
 
297
 
    if ($got) {
298
 
        my $take = ($got < $len) ? $got : $len;
299
 
        $buf  = substr($self->{rbuf}, 0, $take, '');
300
 
        $len -= $take;
301
 
    }
302
 
 
303
 
    while ($len > 0) {
304
 
        $self->can_read
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);
307
 
        if (defined $r) {
308
 
            last unless $r;
309
 
            $len -= $r;
310
 
        }
311
 
        elsif ($! != EINTR) {
312
 
            croak(qq/Could not read from socket: '$!'/);
313
 
        }
314
 
    }
315
 
    if ($len) {
316
 
        croak(q/Unexpected end of stream/);
317
 
    }
318
 
    return $buf;
319
 
}
320
 
 
321
 
sub readline {
322
 
    @_ == 1 || croak(q/Usage: $handle->readline()/);
323
 
    my ($self) = @_;
324
 
 
325
 
    while () {
326
 
        if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
327
 
            return $1;
328
 
        }
329
 
        $self->can_read
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});
332
 
        if (defined $r) {
333
 
            last unless $r;
334
 
        }
335
 
        elsif ($! != EINTR) {
336
 
            croak(qq/Could not read from socket: '$!'/);
337
 
        }
338
 
    }
339
 
    croak(q/Unexpected end of stream while looking for line/);
340
 
}
341
 
 
342
 
sub read_header_lines {
343
 
    @_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/);
344
 
    my ($self, $headers) = @_;
345
 
    $headers ||= {};
346
 
    my $lines   = 0;
347
 
    my $val;
348
 
 
349
 
    while () {
350
 
         my $line = $self->readline;
351
 
 
352
 
         if ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
353
 
             my ($field_name) = lc $1;
354
 
             $val = \($headers->{$field_name} = $2);
355
 
         }
356
 
         elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
357
 
             $val
358
 
               or croak(q/Unexpected header continuation line/);
359
 
             next unless length $1;
360
 
             $$val .= ' ' if length $$val;
361
 
             $$val .= $1;
362
 
         }
363
 
         elsif ($line =~ /\A \x0D?\x0A \z/x) {
364
 
            last;
365
 
         }
366
 
         else {
367
 
            croak(q/Malformed header line: / . $Printable->($line));
368
 
         }
369
 
    }
370
 
    return $headers;
371
 
}
372
 
 
373
 
sub write_header_lines {
374
 
    (@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/);
375
 
    my($self, $headers) = @_;
376
 
 
377
 
    my $buf = '';
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";
384
 
    }
385
 
    $buf .= "\x0D\x0A";
386
 
    return $self->write($buf);
387
 
}
388
 
 
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'};
393
 
 
394
 
    croak("No content-length in the returned response, and this "
395
 
        . "UA doesn't implement chunking") unless defined $len;
396
 
 
397
 
    while ($len > 0) {
398
 
        my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
399
 
        $cb->($self->read($read), $response);
400
 
        $len -= $read;
401
 
    }
402
 
 
403
 
    return;
404
 
}
405
 
 
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'});
410
 
 
411
 
    $len += $self->write($request->{content});
412
 
 
413
 
    $len == $content_length
414
 
      or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/);
415
 
 
416
 
    return $len;
417
 
}
418
 
 
419
 
sub read_response_header {
420
 
    @_ == 1 || croak(q/Usage: $handle->read_response_header()/);
421
 
    my ($self) = @_;
422
 
 
423
 
    my $line = $self->readline;
424
 
 
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));
427
 
 
428
 
    my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
429
 
 
430
 
    return {
431
 
        status   => $status,
432
 
        reason   => $reason,
433
 
        headers  => $self->read_header_lines,
434
 
        protocol => $protocol,
435
 
    };
436
 
}
437
 
 
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) = @_;
441
 
 
442
 
    return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
443
 
         + $self->write_header_lines($headers);
444
 
}
445
 
 
446
 
sub _do_timeout {
447
 
    my ($self, $type, $timeout) = @_;
448
 
    $timeout = $self->{timeout}
449
 
        unless defined $timeout && $timeout >= 0;
450
 
 
451
 
    my $fd = fileno $self->{fh};
452
 
    defined $fd && $fd >= 0
453
 
      or croak(q/select(2): 'Bad file descriptor'/);
454
 
 
455
 
    my $initial = time;
456
 
    my $pending = $timeout;
457
 
    my $nfound;
458
 
 
459
 
    vec(my $fdset = '', $fd, 1) = 1;
460
 
 
461
 
    while () {
462
 
        $nfound = ($type eq 'read')
463
 
            ? select($fdset, undef, undef, $pending)
464
 
            : select(undef, $fdset, undef, $pending) ;
465
 
        if ($nfound == -1) {
466
 
            $! == EINTR
467
 
              or croak(qq/select(2): '$!'/);
468
 
            redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
469
 
            $nfound = 0;
470
 
        }
471
 
        last;
472
 
    }
473
 
    $! = 0;
474
 
    return $nfound;
475
 
}
476
 
 
477
 
sub can_read {
478
 
    @_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/);
479
 
    my $self = shift;
480
 
    return $self->_do_timeout('read', @_)
481
 
}
482
 
 
483
 
sub can_write {
484
 
    @_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/);
485
 
    my $self = shift;
486
 
    return $self->_do_timeout('write', @_)
487
 
}
 
170
} # HTTP::Micro
 
171
 
 
172
{
 
173
   package HTTP::Micro::Handle;
 
174
 
 
175
   use strict;
 
176
   use warnings FATAL => 'all';
 
177
   use English qw(-no_match_vars);
 
178
 
 
179
   use Carp       qw(croak);
 
180
   use Errno      qw(EINTR EPIPE);
 
181
   use IO::Socket qw(SOCK_STREAM);
 
182
 
 
183
   sub BUFSIZE () { 32768 }
 
184
 
 
185
   my $Printable = sub {
 
186
       local $_ = shift;
 
187
       s/\r/\\r/g;
 
188
       s/\n/\\n/g;
 
189
       s/\t/\\t/g;
 
190
       s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
 
191
       $_;
 
192
   };
 
193
 
 
194
   sub new {
 
195
       my ($class, %args) = @_;
 
196
       return bless {
 
197
           rbuf          => '',
 
198
           timeout       => 60,
 
199
           max_line_size => 16384,
 
200
           %args
 
201
       }, $class;
 
202
   }
 
203
 
 
204
   my $ssl_verify_args = {
 
205
       check_cn         => "when_only",
 
206
       wildcards_in_alt => "anywhere",
 
207
       wildcards_in_cn  => "anywhere"
 
208
   };
 
209
 
 
210
   sub connect {
 
211
       @_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/);
 
212
       my ($self, $scheme, $host, $port) = @_;
 
213
 
 
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'};
 
219
       }
 
220
       elsif ( $scheme ne 'http' ) {
 
221
         croak(qq/Unsupported URL scheme '$scheme'\n/);
 
222
       }
 
223
 
 
224
       $self->{fh} = IO::Socket::INET->new(
 
225
           PeerHost  => $host,
 
226
           PeerPort  => $port,
 
227
           Proto     => 'tcp',
 
228
           Type      => SOCK_STREAM,
 
229
           Timeout   => $self->{timeout}
 
230
       ) or croak(qq/Could not connect to '$host:$port': $@/);
 
231
 
 
232
       binmode($self->{fh})
 
233
         or croak(qq/Could not binmode() socket: '$!'/);
 
234
 
 
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 );
 
241
           }
 
242
           else {
 
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/);
 
248
            }
 
249
       }
 
250
         
 
251
       $self->{host} = $host;
 
252
       $self->{port} = $port;
 
253
 
 
254
       return $self;
 
255
   }
 
256
 
 
257
   sub close {
 
258
       @_ == 1 || croak(q/Usage: $handle->close()/);
 
259
       my ($self) = @_;
 
260
       CORE::close($self->{fh})
 
261
         or croak(qq/Could not close socket: '$!'/);
 
262
   }
 
263
 
 
264
   sub write {
 
265
       @_ == 2 || croak(q/Usage: $handle->write(buf)/);
 
266
       my ($self, $buf) = @_;
 
267
 
 
268
       my $len = length $buf;
 
269
       my $off = 0;
 
270
 
 
271
       local $SIG{PIPE} = 'IGNORE';
 
272
 
 
273
       while () {
 
274
           $self->can_write
 
275
             or croak(q/Timed out while waiting for socket to become ready for writing/);
 
276
           my $r = syswrite($self->{fh}, $buf, $len, $off);
 
277
           if (defined $r) {
 
278
               $len -= $r;
 
279
               $off += $r;
 
280
               last unless $len > 0;
 
281
           }
 
282
           elsif ($! == EPIPE) {
 
283
               croak(qq/Socket closed by remote server: $!/);
 
284
           }
 
285
           elsif ($! != EINTR) {
 
286
               croak(qq/Could not write to socket: '$!'/);
 
287
           }
 
288
       }
 
289
       return $off;
 
290
   }
 
291
 
 
292
   sub read {
 
293
       @_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len)/);
 
294
       my ($self, $len) = @_;
 
295
 
 
296
       my $buf  = '';
 
297
       my $got = length $self->{rbuf};
 
298
 
 
299
       if ($got) {
 
300
           my $take = ($got < $len) ? $got : $len;
 
301
           $buf  = substr($self->{rbuf}, 0, $take, '');
 
302
           $len -= $take;
 
303
       }
 
304
 
 
305
       while ($len > 0) {
 
306
           $self->can_read
 
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);
 
309
           if (defined $r) {
 
310
               last unless $r;
 
311
               $len -= $r;
 
312
           }
 
313
           elsif ($! != EINTR) {
 
314
               croak(qq/Could not read from socket: '$!'/);
 
315
           }
 
316
       }
 
317
       if ($len) {
 
318
           croak(q/Unexpected end of stream/);
 
319
       }
 
320
       return $buf;
 
321
   }
 
322
 
 
323
   sub readline {
 
324
       @_ == 1 || croak(q/Usage: $handle->readline()/);
 
325
       my ($self) = @_;
 
326
 
 
327
       while () {
 
328
           if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
 
329
               return $1;
 
330
           }
 
331
           $self->can_read
 
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});
 
334
           if (defined $r) {
 
335
               last unless $r;
 
336
           }
 
337
           elsif ($! != EINTR) {
 
338
               croak(qq/Could not read from socket: '$!'/);
 
339
           }
 
340
       }
 
341
       croak(q/Unexpected end of stream while looking for line/);
 
342
   }
 
343
 
 
344
   sub read_header_lines {
 
345
       @_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/);
 
346
       my ($self, $headers) = @_;
 
347
       $headers ||= {};
 
348
       my $lines   = 0;
 
349
       my $val;
 
350
 
 
351
       while () {
 
352
            my $line = $self->readline;
 
353
 
 
354
            if ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
 
355
                my ($field_name) = lc $1;
 
356
                $val = \($headers->{$field_name} = $2);
 
357
            }
 
358
            elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
 
359
                $val
 
360
                  or croak(q/Unexpected header continuation line/);
 
361
                next unless length $1;
 
362
                $$val .= ' ' if length $$val;
 
363
                $$val .= $1;
 
364
            }
 
365
            elsif ($line =~ /\A \x0D?\x0A \z/x) {
 
366
               last;
 
367
            }
 
368
            else {
 
369
               croak(q/Malformed header line: / . $Printable->($line));
 
370
            }
 
371
       }
 
372
       return $headers;
 
373
   }
 
374
 
 
375
   sub write_header_lines {
 
376
       (@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/);
 
377
       my($self, $headers) = @_;
 
378
 
 
379
       my $buf = '';
 
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";
 
386
       }
 
387
       $buf .= "\x0D\x0A";
 
388
       return $self->write($buf);
 
389
   }
 
390
 
 
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'};
 
395
 
 
396
       croak("No content-length in the returned response, and this "
 
397
           . "UA doesn't implement chunking") unless defined $len;
 
398
 
 
399
       while ($len > 0) {
 
400
           my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
 
401
           $cb->($self->read($read), $response);
 
402
           $len -= $read;
 
403
       }
 
404
 
 
405
       return;
 
406
   }
 
407
 
 
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'});
 
412
 
 
413
       $len += $self->write($request->{content});
 
414
 
 
415
       $len == $content_length
 
416
         or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/);
 
417
 
 
418
       return $len;
 
419
   }
 
420
 
 
421
   sub read_response_header {
 
422
       @_ == 1 || croak(q/Usage: $handle->read_response_header()/);
 
423
       my ($self) = @_;
 
424
 
 
425
       my $line = $self->readline;
 
426
 
 
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));
 
429
 
 
430
       my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
 
431
 
 
432
       return {
 
433
           status   => $status,
 
434
           reason   => $reason,
 
435
           headers  => $self->read_header_lines,
 
436
           protocol => $protocol,
 
437
       };
 
438
   }
 
439
 
 
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) = @_;
 
443
 
 
444
       return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
 
445
            + $self->write_header_lines($headers);
 
446
   }
 
447
 
 
448
   sub _do_timeout {
 
449
       my ($self, $type, $timeout) = @_;
 
450
       $timeout = $self->{timeout}
 
451
           unless defined $timeout && $timeout >= 0;
 
452
 
 
453
       my $fd = fileno $self->{fh};
 
454
       defined $fd && $fd >= 0
 
455
         or croak(q/select(2): 'Bad file descriptor'/);
 
456
 
 
457
       my $initial = time;
 
458
       my $pending = $timeout;
 
459
       my $nfound;
 
460
 
 
461
       vec(my $fdset = '', $fd, 1) = 1;
 
462
 
 
463
       while () {
 
464
           $nfound = ($type eq 'read')
 
465
               ? select($fdset, undef, undef, $pending)
 
466
               : select(undef, $fdset, undef, $pending) ;
 
467
           if ($nfound == -1) {
 
468
               $! == EINTR
 
469
                 or croak(qq/select(2): '$!'/);
 
470
               redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
 
471
               $nfound = 0;
 
472
           }
 
473
           last;
 
474
       }
 
475
       $! = 0;
 
476
       return $nfound;
 
477
   }
 
478
 
 
479
   sub can_read {
 
480
       @_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/);
 
481
       my $self = shift;
 
482
       return $self->_do_timeout('read', @_)
 
483
   }
 
484
 
 
485
   sub can_write {
 
486
       @_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/);
 
487
       my $self = shift;
 
488
       return $self->_do_timeout('write', @_)
 
489
   }
 
490
}  # HTTP::Micro::Handle
488
491
 
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
507
510
   }
508
511
}
509
512
{
 
513
   use Carp qw(croak);
510
514
   my %dispatcher = (
511
515
      issuer =>  sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
512
516
      subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
703
707
}
704
708
 
705
709
1;
706
 
}
707
710
# ###########################################################################
708
711
# End HTTPMicro package
709
712
# ###########################################################################