<?xml version="1.0" encoding="windows-1252"?>
<node id="609632" title="REST Webservices and CGI.pm" created="2007-04-12 07:52:44" updated="2007-04-12 03:52:44">
<type id="115">
perlquestion</type>
<author id="8930">
derby</author>
<data>
<field name="doctext">
&lt;p&gt;&lt;b&gt;update:&lt;/b&gt; As of 2007/11/30, [cpan://CGI] v 3.31 has the patch to accept PUT data correctly. Thanks to prodding from [rhesa], [cpan://CGI] and [cpan://CGI::Simple] now support all the HTTP methods necessary to build REST services.
&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;I finally started building a true REST webservice when I ran smack into a wall. The service is your basic crud service where the HTTP GET will retrieve products, DELETE will delete, POST will update and PUT will create:
&lt;code&gt;
  GET http://foo.com/webservice/&lt;productid&gt;
  DELETE http://foo.com/webservice/&lt;productid&gt;
  POST http://foo.com/webservice/&lt;productid&gt;
  PUT http://foo.com/webservice
&lt;/code&gt;
Nothing out of the ordinary there right? The thing is, I'm a big fan of [mod://CGI::Application] and it uses [mod://CGI] at its core (but that's overridable). The wall is the way [mod://CGI] handles the PUT method (it doesn't really) and the way it handles POST methods -- it's designed for html form parsing. No problem, I thought. [mod://CGI::Application] has the capability to switch out [mod://CGI] with any other module as long as that module adheres to the [mod://CGI] interface (well, not the entire interface).&lt;/p&gt;
&lt;p&gt;So I needed a module that would 
  &lt;ol&gt;
    &lt;li&gt;adhere to the [mod://CGI] interface&lt;/li&gt;
    &lt;li&gt;support the HTTP PUT method&lt;/li&gt;
    &lt;li&gt;not form parse PUT and POST data&lt;/li&gt;
  &lt;/ol&gt;
after much searching, I couldn't find a module for those needs. The closest I came was [mod://CGI::XMLpost] but that wasn't even horseshoe close.&lt;/p&gt;

&lt;p&gt;I finally decided to build one myself but given the nature of CGI, I was pretty sure it wasn't going to be quick and it wasn't going to be pretty. I had used [mod://CGI::Simple] in the past and started thinking if there was a way to co-opt it into what I wanted.
&lt;p&gt;After looking at the code, I figured out all I need to do was override its' _read_parse method and then add accessors for the POST and PUT data. There were only 4 changes needed for _read_parse
  &lt;ul&gt;
    &lt;li&gt;ensure the POST_MAX check is also done when the method is PUT&lt;/li&gt;
    &lt;li&gt;also read from STDIN when the method is PUT&lt;/li&gt;
    &lt;li&gt;don't send PUT and POST data to _parse_params&lt;/li&gt;
    &lt;li&gt;add accessors for the PUT and POST data&lt;/li&gt;
  &lt;/ul&gt;
&lt;/p&gt;

&lt;readmore&gt;
&lt;p&gt;
&lt;code&gt;
package Foo::CGI::Rest;

use base 'CGI::Simple';

sub _read_parse {
  my $self   = shift;
  my $data   = '';
  my $type   = $ENV{'CONTENT_TYPE'} || 'No CONTENT_TYPE received';
  my $length = $ENV{'CONTENT_LENGTH'} || 0;
  my $method = $ENV{'REQUEST_METHOD'} || 'No REQUEST_METHOD received';
  
  # change #1 - added or "PUT" here ... we don't want
  # malicious PUTs either

 # first check POST_MAX Steve Purkis pointed out the previous bug
  if( ( $method eq 'POST' or $method eq "PUT" )
      and $self-&gt;{'.globals'}-&gt;{'POST_MAX'} != -1
      and $length &gt; $self-&gt;{'.globals'}-&gt;{'POST_MAX'}) {
      $self-&gt;cgi_error(
        "413 Request entity too large: $length bytes on STDIN exceeds \$POST_MAX!"
      );

      # silently discard data ??? better to just close the socket ???
      while ($length &gt; 0) {
        last unless sysread(STDIN, my $buffer, 4096);
        $length -= length($buffer);
      }
      return;
  }

  if( $length and $type =~ m|^multipart/form-data|i ) {
    my $got_length = $self-&gt;_parse_multipart;
    if( $length != $got_length ) {
      $self-&gt;cgi_error("500 Bad read on multipart/form-data! wanted $length, got $got_length");
    }

  # changed #2 - or "PUT" here too
  } elsif( $method eq 'POST' or $method eq 'PUT' ) {
    if( $length ) {
      # we may not get all the data we want with a single read on large
      # POSTs as it may not be here yet! Credit Jason Luther for patch
      # CGI.pm &lt; 2.99 suffers from same bug
      sysread(STDIN, $data, $length);
      while( length($data) &lt; $length ) {
        last unless sysread(STDIN, my $buffer, 4096);
        $data .= $buffer;
      }
      # change 3 - don't send data to parse params ... it's not form data
      if( $length == length $data ) {
        $self-&gt;set_data( $data );
      } else {
        $self-&gt;cgi_error("500 Bad read on POST! wanted $length, got " . length($data));
      }
    }
  } elsif( $method eq 'GET' or $method eq 'HEAD' ) {
    $data =
      $self-&gt;{'.mod_perl'}
      ? $self-&gt;_mod_perl_request()-&gt;args()
      : $ENV{'QUERY_STRING'}
      || $ENV{'REDIRECT_QUERY_STRING'}
      || '';
    $self-&gt;_parse_params($data);
  } else {
    unless ($self-&gt;{'.globals'}-&gt;{'DEBUG'}
      and $data = $self-&gt;read_from_cmdline()) {
      $self-&gt;cgi_error("400 Unknown method $method");
    }
  }
}

# change 4 - create accessors
sub set_data {
  my( $self, $data ) = @_;
  $self-&gt;{_data} = $data;
}

sub get_data {
  my( $self ) = @_;
  return $self-&gt;{_data};
}

1;
&lt;/code&gt;

Now in my [mod://CGI::Application] all I have to do is 

&lt;code&gt;
sub cgiapp_get_query {
  my $self = shift;
  require Foo::CGI::Rest;
  return  Foo::CGI::Rest-&gt;new();
}
&lt;/code&gt;

and in my handlers for POST and PUT:

&lt;code&gt;
sub update {
  my $self   = shift;
  my $cgi    = $self-&gt;query();
  my $xmlstr = $cgi-&gt;get_data();
  ...
}

sub create {
  my $self   = shift;
  my $cgi    = $self-&gt;query();
  my $xmlstr = $cgi-&gt;get_data();
  ...
}
&lt;/code&gt;
&lt;/p&gt;
&lt;/readmore&gt;


&lt;p&gt;
The thing that worries me though, is REST has been around a few years now and [mod://CGI] has been around forever. So any ideas why [mod://CGI] and it's derivatives treat PUT like a read headed step child?
&lt;/p&gt;

&lt;!-- Node text goes above. Div tags should contain sig only --&gt;
&lt;div class="pmsig"&gt;&lt;div class="pmsig-8930"&gt;
-derby
&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;b&gt;Update:&lt;/b&gt; Updated title.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Update:&lt;/b&gt; Given the great feedback from [id://448370], I've further simplified the _read_parse override by setting the POSTDATA and PUTDATA params if the POST'ed and PUT'ed data is not of type 'application/x-www-form-urlencoded' ... hmmm maybe I should submit this as a patch to the [mod://CGI::Simple] author.
Here it is in all it's simpleness:
&lt;readmore&gt;
&lt;code&gt;
package Foo::CGI::Rest;

use base 'CGI::Simple';

sub _read_parse {
  my $self   = shift;
  my $data   = '';
  my $type   = $ENV{'CONTENT_TYPE'} || 'No CONTENT_TYPE received';
  my $length = $ENV{'CONTENT_LENGTH'} || 0;
  my $method = $ENV{'REQUEST_METHOD'} || 'No REQUEST_METHOD received';

  # first check POST_MAX Steve Purkis pointed out the previous bug
  if( ( $method eq 'POST' or $method eq "PUT" )
      and $self-&gt;{'.globals'}-&gt;{'POST_MAX'} != -1
      and $length &gt; $self-&gt;{'.globals'}-&gt;{'POST_MAX'}) {
      $self-&gt;cgi_error(
        "413 Request entity too large: $length bytes on STDIN exceeds \$POST_MAX!"
      );

      # silently discard data ??? better to just close the socket ???
      while ($length &gt; 0) {
        last unless sysread(STDIN, my $buffer, 4096);
        $length -= length($buffer);
      }
      return;
  }

  if( $length and $type =~ m|^multipart/form-data|i ) {
    my $got_length = $self-&gt;_parse_multipart;
    if( $length != $got_length ) {
      $self-&gt;cgi_error("500 Bad read on multipart/form-data! wanted $length, got $got_length");
    }
  } elsif( $method eq 'POST' or $method eq 'PUT' ) {
    if( $length ) {
      # we may not get all the data we want with a single read on large
      # POSTs as it may not be here yet! Credit Jason Luther for patch
      # CGI.pm &lt; 2.99 suffers from same bug
      sysread(STDIN, $data, $length);
      while( length($data) &lt; $length ) {
        last unless sysread(STDIN, my $buffer, 4096);
        $data .= $buffer;
      }
      if( $length == length $data ) {
        if( $type !~ m|^application/x-www-form-urlencoded| ) {
          $self-&gt;_add_param( $method . "DATA", $data );
        } else {
          $self-&gt;_parse_params( $data );
        }
      } else {
        $self-&gt;cgi_error("500 Bad read on POST! wanted $length, got " . length($data));
      }
    }
  } elsif( $method eq 'GET' or $method eq 'HEAD' ) {
    $data =
      $self-&gt;{'.mod_perl'}
      ? $self-&gt;_mod_perl_request()-&gt;args()
      : $ENV{'QUERY_STRING'}
      || $ENV{'REDIRECT_QUERY_STRING'}
      || '';
    $self-&gt;_parse_params($data);
  } else {
    unless ($self-&gt;{'.globals'}-&gt;{'DEBUG'}
      and $data = $self-&gt;read_from_cmdline()) {
      $self-&gt;cgi_error("400 Unknown method $method");
    }
  }
}

1;
&lt;/code&gt;
&lt;/readmore&gt;
&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Update:&lt;/b&gt; Submitted a patch to the author of [mod://CGI::Simple]&lt;/p&gt;</field>
</data>
</node>
