nss12 has asked for the wisdom of the Perl Monks concerning the following question:

Hey, I was wondering if I could get some help with a little web server I'm working on. I have things working for normal requests and GET data, but I'm having trouble accepting POST data. So, I'm trying to figure out a way of reading the socket to retrieve the post data sent along with a request, at the moment I'm just getting the header - hence why it's working for GET. My code is below. Does anybody know of any way or ideas to grab POST data sent in a HTTP Request? Many thanks.
use Socket; use strict; use warnings; use IO::Select; use threads; use threads::shared; # Program Options $| = 1; # Whether or not to flush the output - 1 = yes, 0 = no my $HTTP_PORT = 80; # Port to listen to for HTTP requests my $SSL_PORT = 443; # Port to listen to for encrypted HTTP requsts my $USE_THREADING = 0; # Create a new thread for each request #setup the http port for use with interface local *S; socket (S, PF_INET , SOCK_STREAM , getprotobyname('tcp')) or die + "couldn't open socket: $!"; setsockopt (S, SOL_SOCKET, SO_REUSEADDR, 1) or die "No Setting."; bind (S, sockaddr_in($HTTP_PORT, INADDR_ANY)); listen (S, 5) or die + "don't hear anything: $!"; my $ss = IO::Select->new(); $ss -> add (*S); #my $sse = IO::Select->new(); #$sse -> add (*S); if($USE_THREADING){ #asynchronous loop while(1){ #continually grab requests from port my @connections_pending = $ss->can_read(); foreach (@connections_pending) { #grab the header of the request my $fh; my $remote = accept($fh, $_); #get the remote port and ip my($port,$iaddr) = sockaddr_in($remote); my $peeraddress = inet_ntoa($iaddr); #put the remote ip with header my @sendArray = ($fh, $peeraddress); #create a new thread to handle this request my $t = threads->create(\&new_connection, @sendArray); #don't leak memory! $t->detach(); } } } #method called on each new connection sub new_connection { #take the request header from parameter array my $fh = shift; #take the remote ip from parameter array my $remote_addr = shift; #read header in binary binmode $fh; #create a list for header vars my %req; $req{HEADER}={}; my $request_line = <$fh>; my $first_line = ""; while ($request_line ne "\r\n") { unless ($request_line) { close $fh; } chomp $request_line; #add the remote ip var to header $req{HEADER}{"remote-ip"} = $remote_addr; unless ($first_line) { $first_line = $request_line; my @parts = split(" ", $first_line); if (@parts != 3) { close $fh; } #add method (get/post) to header with objects $req{METHOD} = $parts[0]; $req{OBJECT} = $parts[1]; } else { my ($name, $value) = split(": ", $request_line); $name = lc $name; #add each variable to the list $req{HEADER}{$name} = $value; } $request_line = <$fh>; } #call the HTTP request handler method http_request_handler($fh, \%req); close $fh; }

Replies are listed 'Best First'.
Re: Getting POST data from HTTP Request
by tobyink (Canon) on May 14, 2012 at 06:26 UTC

    You don't show the code for http_request_handler. That function appears to be passed a filehandle to read the HTTP body from. The filehandle is unlikely to produce a proper EOF marker, so the request handler should peek at $req->{HEADER}{'Content-Length'}.

    Any particular reason you're not using HTTP::Daemon, HTTP::Daemon::SSL, HTTP::Server::Simple, Starlet, Starman, Gepok or any other of the fine HTTP servers on CPAN? You'll save yourself a lot of work and get a better quality solution. There are, for example, at least two bugs in your parsing of HTTP headers.

    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
      Thanks for the reply. The main aim was really to play around and learn, eventually perhaps incorporating it into another project. Thanks for the EOF tip too, using content length seems to do the trick, but you're right in that it's a little fragile. Perhaps I'll move onto using one of the CPAN HTTP servers - saves bother now that I've satisfied my need for experimentation. Cheers.