CGI-Tiny

 view release on metacpan or  search on metacpan

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

package CGI::Tiny;
# ABSTRACT: Common Gateway Interface, with no frills

# This file is part of CGI::Tiny which is released under:
#   The Artistic License 2.0 (GPL Compatible)
# See the documentation for CGI::Tiny for full license details.

use strict;
use warnings;
use Carp ();
use IO::Handle ();
use Exporter ();

our $VERSION = '1.003';

use constant DEFAULT_REQUEST_BODY_LIMIT => 16777216;
use constant DEFAULT_REQUEST_BODY_BUFFER => 262144;
use constant DEFAULT_RESPONSE_BODY_BUFFER => 131072;

our @EXPORT = 'cgi';

# List from HTTP::Status 6.46
# Unmarked codes are from RFC 7231 (2017-12-20)
my %HTTP_STATUS = (
    100 => 'Continue',
    101 => 'Switching Protocols',
    102 => 'Processing',                      # RFC 2518: WebDAV
    103 => 'Early Hints',                     # RFC 8297: Indicating Hints
    200 => 'OK',
    201 => 'Created',
    202 => 'Accepted',
    203 => 'Non-Authoritative Information',
    204 => 'No Content',
    205 => 'Reset Content',
    206 => 'Partial Content',                 # RFC 7233: Range Requests
    207 => 'Multi-Status',                    # RFC 4918: WebDAV
    208 => 'Already Reported',                # RFC 5842: WebDAV bindings
    226 => 'IM Used',                         # RFC 3229: Delta encoding
    300 => 'Multiple Choices',
    301 => 'Moved Permanently',
    302 => 'Found',
    303 => 'See Other',
    304 => 'Not Modified',                    # RFC 7232: Conditional Request
    305 => 'Use Proxy',
    307 => 'Temporary Redirect',
    308 => 'Permanent Redirect',              # RFC 9110: HTTP Semantics
    400 => 'Bad Request',
    401 => 'Unauthorized',                    # RFC 7235: Authentication
    402 => 'Payment Required',
    403 => 'Forbidden',
    404 => 'Not Found',
    405 => 'Method Not Allowed',
    406 => 'Not Acceptable',
    407 => 'Proxy Authentication Required',   # RFC 7235: Authentication
    408 => 'Request Timeout',
    409 => 'Conflict',
    410 => 'Gone',
    411 => 'Length Required',
    412 => 'Precondition Failed',             # RFC 7232: Conditional Request
    413 => 'Content Too Large',               # RFC 9110: HTTP Semantics
    414 => 'URI Too Long',
    415 => 'Unsupported Media Type',
    416 => 'Range Not Satisfiable',           # RFC 7233: Range Requests
    417 => 'Expectation Failed',
    418 => 'I\'m a teapot',                   # RFC 2324: HTCPC/1.0  1-april
    421 => 'Misdirected Request',             # RFC 9110: HTTP Semantics
    422 => 'Unprocessable Content',           # RFC 9110: HTTP Semantics
    423 => 'Locked',                          # RFC 4918: WebDAV
    424 => 'Failed Dependency',               # RFC 4918: WebDAV
    425 => 'Too Early',                       # RFC 8470: Using Early Data in HTTP
    426 => 'Upgrade Required',
    428 => 'Precondition Required',           # RFC 6585: Additional Codes
    429 => 'Too Many Requests',               # RFC 6585: Additional Codes
    431 => 'Request Header Fields Too Large', # RFC 6585: Additional Codes
    451 => 'Unavailable For Legal Reasons',   # RFC 7725: Legal Obstacles
    500 => 'Internal Server Error',
    501 => 'Not Implemented',
    502 => 'Bad Gateway',
    503 => 'Service Unavailable',
    504 => 'Gateway Timeout',
    505 => 'HTTP Version Not Supported',
    506 => 'Variant Also Negotiates',         # RFC 2295: Transparant Ngttn
    507 => 'Insufficient Storage',            # RFC 4918: WebDAV
    508 => 'Loop Detected',                   # RFC 5842: WebDAV bindings
    509 => 'Bandwidth Limit Exceeded',        #           Apache / cPanel
    510 => 'Not Extended',                    # RFC 2774: Extension Framework
    511 => 'Network Authentication Required', # RFC 6585: Additional Codes
);

{
  my $cgi;

  sub import {
    # for cleanup in END in case of premature exit
    $cgi ||= bless {pid => $$}, $_[0];
    goto &Exporter::import;
  }

  sub cgi (&) {
    my ($handler) = @_;
    $cgi ||= bless {pid => $$}, __PACKAGE__;
    if (@ARGV and !defined $ENV{REQUEST_METHOD}) {
      require CGI::Tiny::_Debug;
      CGI::Tiny::_Debug::debug_command($cgi, [@ARGV]);
    }
    my ($error, $errored);
    {
      local $@;
      eval { local $_ = $cgi; $handler->(); 1 } or do { $error = $@; $errored = 1 };
    }
    if ($errored) {
      _handle_error($cgi, $error);
    } elsif (!$cgi->{headers_rendered}) {
      _handle_error($cgi, "cgi completed without rendering a response\n");
    }
    undef $cgi;
    1;
  }

  # cleanup of premature exit, more reliable than potentially doing this in global destruction
  # ModPerl::Registry or CGI::Compile won't run END after each request,
  # but they override exit to throw an exception which we handle already
  END {
    if (defined $cgi) {
      _handle_error($cgi, "cgi exited without rendering a response\n") unless $cgi->{headers_rendered};
      undef $cgi;
    }
  }
}

sub _handle_error {
  my ($cgi, $error) = @_;
  return unless $cgi->{pid} == $$; # in case of fork
  $cgi->{response_status} = "500 $HTTP_STATUS{500}" unless $cgi->{headers_rendered}
    or (defined $cgi->{response_status} and $cgi->{response_status} =~ m/^[45][0-9]{2} /);
  if (defined(my $handler = $cgi->{on_error})) {
    my ($error_error, $error_errored);
    {
      local $@;
      eval { $handler->($cgi, $error, !!$cgi->{headers_rendered}); 1 } or do { $error_error = $@; $error_errored = 1 };



( run in 2.620 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )