CGI-Tiny

 view release on metacpan or  search on metacpan

lib/CGI/Tiny.pm  view on Meta::CPAN

  unless (exists $self->{body_params}) {
    $self->{body_params} = {names => \my @names, ordered => \my @ordered, keyed => \my %keyed};
    if ($ENV{CONTENT_TYPE} and $ENV{CONTENT_TYPE} =~ m/^application\/x-www-form-urlencoded\b/i) {
      foreach my $pair (split /&/, $self->body) {
        my ($name, $value) = split /=/, $pair, 2;
        $value = '' unless defined $value;
        do { tr/+/ /; s/%([0-9a-fA-F]{2})/chr hex $1/ge; utf8::decode $_ } for $name, $value;
        push @names, $name unless exists $keyed{$name};
        push @ordered, [$name, $value];
        push @{$keyed{$name}}, $value;
      }
    } elsif ($ENV{CONTENT_TYPE} and $ENV{CONTENT_TYPE} =~ m/^multipart\/form-data\b/i) {
      my $default_charset = $self->{multipart_form_charset};
      $default_charset = 'UTF-8' unless defined $default_charset;
      foreach my $part (@{$self->_body_multipart}) {
        next if defined $part->{filename};
        my ($name, $headers, $content, $file) = @$part{'name','headers','content','file'};
        if (length $default_charset) {
          require Encode;
          $name = Encode::decode($default_charset, "$name");
        }
        my $value = '';
        if (defined $content) {
          $value = $content;
        } elsif (defined $file) {
          binmode $file;
          seek $file, 0, 0;
          $value = do { local $/; readline $file };
          seek $file, 0, 0;
        }
        my $value_charset;
        if (defined $headers->{'content-type'}) {
          if (my ($charset_quoted, $charset_unquoted) = $headers->{'content-type'} =~ m/;\s*charset=(?:"((?:\\[\\"]|[^"])+)"|([^";]+))/i) {
            $charset_quoted =~ s/\\([\\"])/$1/g if defined $charset_quoted;
            $value_charset = defined $charset_quoted ? $charset_quoted : $charset_unquoted;
          }
        }
        if (defined $value_charset or !defined $headers->{'content-type'} or $headers->{'content-type'} =~ m/^text\/plain\b/i) {
          require Encode;
          if (defined $value_charset) {
            $value = Encode::decode($value_charset, "$value");
          } elsif (length $default_charset) {
            $value = Encode::decode($default_charset, "$value");
          }
        }
        push @names, $name unless exists $keyed{$name};
        push @ordered, [$name, $value];
        push @{$keyed{$name}}, $value;
      }
    }
  }
  return $self->{body_params};
}

sub body_parts {
  my ($self) = @_;
  return [] unless $ENV{CONTENT_TYPE} and $ENV{CONTENT_TYPE} =~ m/^multipart\/form-data\b/i;
  return [map { +{%$_} } @{$self->_body_multipart}];
}

sub uploads      { [map { [@$_] } @{$_[0]->_body_uploads->{ordered}}] }
sub upload_names { [@{$_[0]->_body_uploads->{names}}] }
sub upload       { my $u = $_[0]->_body_uploads->{keyed}; exists $u->{$_[1]} ? $u->{$_[1]}[-1] : undef }
sub upload_array { my $u = $_[0]->_body_uploads->{keyed}; exists $u->{$_[1]} ? [@{$u->{$_[1]}}] : [] }

sub _body_uploads {
  my ($self) = @_;
  unless (exists $self->{body_uploads}) {
    $self->{body_uploads} = {names => \my @names, ordered => \my @ordered, keyed => \my %keyed};
    if ($ENV{CONTENT_TYPE} and $ENV{CONTENT_TYPE} =~ m/^multipart\/form-data\b/i) {
      my $default_charset = $self->{multipart_form_charset};
      $default_charset = 'UTF-8' unless defined $default_charset;
      foreach my $part (@{$self->_body_multipart}) {
        next unless defined $part->{filename};
        my ($name, $filename, $size, $headers, $file, $content) = @$part{'name','filename','size','headers','file','content'};
        if (length $default_charset) {
          require Encode;
          $name = Encode::decode($default_charset, "$name");
          $filename = Encode::decode($default_charset, "$filename");
        }
        my $upload = {
          filename     => $filename,
          size         => $size,
          content_type => $headers->{'content-type'},
        };
        $upload->{file} = $file if defined $file;
        $upload->{content} = $content if defined $content;
        push @names, $name unless exists $keyed{$name};
        push @ordered, [$name, $upload];
        push @{$keyed{$name}}, $upload;
      }
    }
  }
  return $self->{body_uploads};
}

sub _body_length {
  my ($self) = @_;
  my $limit = $self->{request_body_limit};
  $limit = $ENV{CGI_TINY_REQUEST_BODY_LIMIT} unless defined $limit;
  $limit = DEFAULT_REQUEST_BODY_LIMIT unless defined $limit;
  my $length = $ENV{CONTENT_LENGTH} || 0;
  if ($limit and $length > $limit) {
    $self->{response_status} = "413 $HTTP_STATUS{413}" unless $self->{headers_rendered};
    die "Request body limit exceeded\n";
  }
  return 0 + $length;
}

sub _body_multipart {
  my ($self) = @_;
  unless (exists $self->{body_parts}) {
    $self->{body_parts} = [];
    require CGI::Tiny::Multipart;
    my $boundary = CGI::Tiny::Multipart::extract_multipart_boundary($ENV{CONTENT_TYPE});
    unless (defined $boundary) {
      $self->{response_status} = "400 $HTTP_STATUS{400}" unless $self->{headers_rendered};
      die "Malformed multipart/form-data request\n";
    }

    my ($input, $length);
    if (exists $self->{body_content}) {
      $length = length $self->{body_content};
      $input = \$self->{body_content};
    } else {
      $length = $self->_body_length;
      $input = defined $self->{input_handle} ? $self->{input_handle} : *STDIN;
    }

    my $parts = CGI::Tiny::Multipart::parse_multipart_form_data($input, $length, $boundary, {
      buffer_size => $self->{request_body_buffer} || $ENV{CGI_TINY_REQUEST_BODY_BUFFER},
      %{$self->{multipart_form_options} || {}},
    });
    unless (defined $parts) {
      $self->{response_status} = "400 $HTTP_STATUS{400}" unless $self->{headers_rendered};
      die "Malformed multipart/form-data request\n";
    }

    $self->{body_parts} = $parts;
  }
  return $self->{body_parts};
}

sub set_nph {
  my ($self, $value) = @_;
  if ($self->{headers_rendered}) {
    Carp::carp "Attempted to set NPH response mode but headers have already been rendered";
  } else {
    $self->{nph} = @_ < 2 ? 1 : $value;
  }
  return $self;
}

sub set_response_body_buffer { $_[0]{response_body_buffer} = $_[1]; $_[0] }



( run in 0.766 second using v1.01-cache-2.11-cpan-b16cb0d3907 )