The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package WWW::Curl::UserAgent::Request;
{
  $WWW::Curl::UserAgent::Request::VERSION = '0.9.5';
}

use Moose;
use WWW::Curl::Easy;

has http_request => (
    is       => 'ro',
    isa      => 'HTTP::Request',
    required => 1,
);

has connect_timeout => (
    is       => 'ro',
    isa      => 'Int',
    required => 1,
);

has timeout => (
    is       => 'ro',
    isa      => 'Int',
    required => 1,
);

has keep_alive => (
    is       => 'ro',
    isa      => 'Bool',
    required => 1,
);

has followlocation => (
    is      => 'ro',
    isa     => 'Bool',
    default => 0,
);

has max_redirects => (
    is      => 'ro',
    isa     => 'Int',
    default => -1,
);

has curl_easy => (
    is         => 'ro',
    isa        => 'WWW::Curl::Easy',
    lazy_build => 1,
);

has header_ref => (
    is      => 'ro',
    isa     => 'Ref',
    default => sub { my $header; \$header },
);

has content_ref => (
    is      => 'ro',
    isa     => 'Ref',
    default => sub { my $content; \$content },
);

sub _build_curl_easy {
    my $self = shift;

    my $easy    = WWW::Curl::Easy->new;
    my $request = $self->http_request;

    $easy->setopt( CURLOPT_CONNECTTIMEOUT_MS, $self->connect_timeout );
    $easy->setopt( CURLOPT_HEADER,            0 );
    $easy->setopt( CURLOPT_NOPROGRESS,        1 );
    $easy->setopt( CURLOPT_TIMEOUT_MS,        $self->timeout );
    $easy->setopt( CURLOPT_URL,               $request->uri );
    $easy->setopt( CURLOPT_WRITEHEADER,       $self->header_ref );
    $easy->setopt( CURLOPT_WRITEDATA,         $self->content_ref );
    $easy->setopt( CURLOPT_FORBID_REUSE,      !$self->keep_alive );
    $easy->setopt( CURLOPT_FOLLOWLOCATION,    $self->followlocation );
    $easy->setopt( CURLOPT_MAXREDIRS,         $self->max_redirects );

    # see https://github.com/pauldix/typhoeus/blob/master/lib/typhoeus/easy.rb#L197
    if ( $request->method eq 'GET' ) {
        $easy->setopt( CURLOPT_HTTPGET, 1 );
    }
    elsif ( $request->method eq 'POST' ) {
        use bytes;
        my $content = $request->content;
        $easy->setopt( CURLOPT_POST,           1 );
        $easy->setopt( CURLOPT_POSTFIELDSIZE,  length $content );
        $easy->setopt( CURLOPT_COPYPOSTFIELDS, $content );
    }
    elsif ( $request->method eq 'PUT' ) {
        use bytes;
        my $content = $request->content;
        $easy->setopt( CURLOPT_UPLOAD,        1 );
        $easy->setopt( CURLOPT_INFILE,        \$content );
        $easy->setopt( CURLOPT_INFILESIZE,    length $content );
        $easy->setopt( CURLOPT_READFUNCTION,  \&_read_callback );
        $easy->setopt( CURLOPT_WRITEFUNCTION, \&_chunk_callback );
    }
    elsif ( $request->method eq 'HEAD' ) {
        $easy->setopt( CURLOPT_NOBODY, 1 );
    }
    else {
        $easy->setopt( CURLOPT_CUSTOMREQUEST, uc $request->method );
    }

    my @headers;
    foreach my $h ( +$request->headers->header_field_names ) {
        push( @headers, "$h: " . $request->header($h) );
    }
    push @headers, "Connection: close" unless $self->keep_alive;
    $easy->setopt( CURLOPT_HTTPHEADER, \@headers )
        if scalar(@headers);

    return $easy;
}

sub _read_callback {
    my ( $maxlength, $pointer ) = @_;
    my $data = substr( $$pointer, 0, $maxlength );
    $$pointer =
      length($$pointer) > $maxlength
      ? scalar substr( $$pointer, $maxlength )
      : '';
    return $data;
}

sub _chunk_callback {
    my ( $data, $pointer ) = @_;
    ${$pointer} .= $data;
    return length($data);
}

no Moose;
__PACKAGE__->meta->make_immutable;
1;