From 8f27e65bddd7d4b8515ce620fb485fdd78fcdf89 Mon Sep 17 00:00:00 2001 From: Constantin Jucovschi Date: Fri, 24 Apr 2009 07:20:22 -0400 Subject: Initial commit --- httpserver/http-doit.cc | 536 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 536 insertions(+) create mode 100644 httpserver/http-doit.cc (limited to 'httpserver/http-doit.cc') diff --git a/httpserver/http-doit.cc b/httpserver/http-doit.cc new file mode 100644 index 0000000..1136fb6 --- /dev/null +++ b/httpserver/http-doit.cc @@ -0,0 +1,536 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see . +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see +* or contact Peter Baumann via . +*/ + +#include "mymalloc/mymalloc.h" +/*------------------------------------------------------------------------*/ +/* http-doit.c - functions for handling the communication in its */ +/* various states. */ +/*------------------------------------------------------------------------*/ + +#include "servercomm/httpserver.hh" + +#include "defs.h" +#include "protos.h" +#include "server.h" +#include "http-defs.h" +#include "http.h" + +extern struct ServerBase Server; + +/****** http/Accept ********************************************************** +* +* NAME +* Accept -- wrapper function to `accept()' system call. +* +* SYNOPSIS +* rc_t Accept( int SockFD, struct ClientBase *Client ); +* +* FUNCTION +* This function is used as a wrapper for the `accept()' system call so +* the appropriate variables and structures as defined for this program +* will be used. In addition, if `accept()' fails it will lead the +* program into immediate abortion. +* +* INPUTS +* SockFD - the file descriptor of the socket, which will be used to +* accept connections. +* Client - a pointer to a ClientBase structure, where all necessary +* informations of the client who made the connection is stored. +* +* RESULT +* Returns the "OK" status code on success, never returns if it fails. +* As a side effect, when `accept()' returns, the socket structure that +* is part of the ClientBase structure is setup by `accpet()'. +* +* NOTES +* "Client" must be a pointer to a valid ClientBase structure. +* Calls `ErrorMsg()' in case of an failure with option fail, which will +* cause an immediate abortion of the program. +* +* BUGS +* 1) Does not check if "Client" is a NULL pointer. +* 2) On some systems the accept call has to be made twice to succeed, +* because the first attempt fails with an "interrupted system call" +* error message. +* +****************************************************************************** +* +*/ + +rc_t Accept( int SockFD, struct ClientBase *Client ) +{ + short cc = 0; + int test = -1; + + if( Client == NULL ) + ErrorMsg( E_SYS, FAIL, "FAIL: accept(): Client is NULL." ); + Client->SockSize = sizeof( Client->Socket ); + + while (( cc < 10 ) && ( test < 0 )) + { +#ifdef DECALPHA + test = accept( SockFD, (struct sockaddr *)&Client->Socket, + (int*)&Client->SockSize ); +#else + test = accept( SockFD, (struct sockaddr *)&Client->Socket, + (socklen_t*)&Client->SockSize ); +#endif + cc++; + LogMsg( LG_SERVER, DEBUG, "DEBUG: accept call # %d",cc ); + } + Client->SockFD = test; + if( Client->SockFD < 0 ) + ErrorMsg( E_SYS, FAIL, "FAIL: accept()." ); + return( OK ); +} + + + + +void GetRequest( struct ClientBase *Client ) +{ + int flags; + + /*-- Try to make SockFD non-blocking; */ + /* if it fails, try to continue nevertheless --*/ + flags = fcntl( Client->SockFD, F_GETFL, 0 ); + if( flags < 0 ) + ErrorMsg( E_SYS, WARN, + "WARN: GetRequest(): Client Socket: fcntl() (get flags) failed." ); + else if( fcntl( Client->SockFD, F_SETFL, flags | O_NONBLOCK ) < 0 ) + ErrorMsg( E_SYS, WARN, + "WARN: GetRequest(): Client Socket: fcntl() (set to non-blocking) failed." ); + + /*-- Read Header of Request */ + Client->Request.State = ReadHeader( Client->SockFD, + &Client->Request.HeadBuff, + &Client->Request.HeadSize ); + //LogMsg( LG_SERVER, DEBUG, "DEBUG: Header is:\n %s",Client->Request.HeadBuff ); +} + + + + +void InterpreteRequest( struct ClientBase *Client, struct ToDoArgs *ToDo ) +{ + struct CacheData *Data = NULL; + + switch( Client->Request.State ) + { + case RI_PARSE_ERROR: + // The request couldn't be parsed (comm. error or something similar) + // --> Shutdown the connection. + Client->Comm.Protocol = HTTP_1_0; + Client->Comm.ConnStatus = CONN_ERROR; + ToDo->What = DO_SHUTDOWN; + ToDo->Which.Code = CLOSE_CLIENT_ONLY; + break; + case RI_PARSE_OK: + // Check for Protocol Version of Client + // May be a HTTP/0.9 request? + if( Client->Request.Line.Version.Major <= 0 ) + { + // Yes -- we don't support it, would be too much trouble ahead... + ToDo->What = DO_SEND_ERROR; + ToDo->Which.Code = STATUS_HTTP_Version_Not_Supported; + return; + } + else if( Client->Request.Line.Version.Major == 1 ) + { + Client->Comm.Protocol = HTTP_1_0; + } + else + { + ToDo->What = DO_SEND_ERROR; + ToDo->Which.Code = STATUS_HTTP_Version_Not_Supported; + return; + } + + // Check for Content-Length and if available, then ReadBody() + Client->Request.BodySize = GetContentLength( Client->Request.First ); + if( Client->Request.BodySize > 0 ) + { + //LogMsg( LG_SERVER, DEBUG, "DEBUG: InterpreteRequest() -> ReadBody()." ); + Client->Request.BodyBuff = ReadBody( Client->SockFD, Client->Request.BodySize ); + } + // Get Connection Status //### currently not really... + if( TRUE ) + Client->Comm.ConnStatus = CONN_CLOSE; + + // Check for supported Methods + if( Client->Request.Line.Method != MKEY_POST ) + { + // No supported Method found... + //LogMsg( LG_SERVER, DEBUG, "DEBUG: No post request!" ); + ToDo->What = DO_SEND_ERROR; + ToDo->Which.Code = STATUS_Method_Not_Allowed; + } + // Send response + else + { + ToDo->What = DO_SEND_RESPONSE; + ToDo->Which.Code = REQU_OK; + } + break; + default: + // Strange State... + ErrorMsg( E_PRIV, ERROR, + "ERROR: InterpreteRequest(): Request can't be handled!" ); + Client->Comm.ConnStatus = CONN_ERROR; + ToDo->What = DO_SHUTDOWN; + ToDo->Which.Code = CLOSE_CLIENT_ONLY; + break; + } +} + + + +char *ComposeErrorResponse( int Errno, int ClientType ) +{ + char *Msg; + char *Buff; + size_t BuffSize = BUFFSIZE; + + if( ( Msg = (char*)mymalloc( BuffSize ) ) == NULL ) + { + ErrorMsg( E_SYS, ERROR, + "ERROR: ComposeErrorMsg() - malloc() error for Msg Buffer buffer." ); + } + BuffSize -= 1; + Buff = Msg; + + switch( ClientType ) + { + case BROWSER: + /* Fehlermeldungen fuer HTML-Browser */ + SNPrintf( Buff, &BuffSize, "

Error %d: ", Errno ); + Buff = Msg + strlen( Msg ); + switch( Errno ) + { + case REQU_UNKNOWN_PARAMETER: + SNPrintf( Buff, &BuffSize, "Unknown protocol parameter." ); + break; + default: + SNPrintf( Buff, &BuffSize, "Undefined protocol error." ); + break; + } + break; + + case RASCLIENT: + default: + /* Fehlermeldungen fuer RasClients */ + SNPrintf( Buff, &BuffSize, "%d+%d+%d", RESULT_ERROR, Errno, 0 ); + break; + } + + //LogMsg( LG_SERVER, DEBUG, "DEBUG: ComposeErrorMsg() returns %s", Msg ); + return Msg; +} + + + +void SendResponse( struct ClientBase *Client ) +{ + + /* + LogMsg( LG_SERVER, DEBUG, "DEBUG: SendResponse() : %s", + Client->Response.Body ); + */ + + SendHTTPMsg( Client->SockFD, &Client->Response ); +} + + +/********************************************************************* + * + * This function interpretes the body of an incoming request + * and fills the RequestInfo struct accordingly. + * + *********************************************************************/ +rc_t GetHTTPRequest( char *Source, int SourceLen, struct HTTPRequest *RequestInfo) +{ + char *Buffer = NULL; + char *Input; + + Input = (char*)mymalloc( SourceLen + 1 ); + memcpy( Input, Source, SourceLen ); + Input[SourceLen] = '\0'; + // Read the message body and check for the post parameters + Buffer = strtok( Input, "=" ); + while( Buffer != NULL ) + { + if( strcmp(Buffer,"Database") == 0 ) + { + RequestInfo->Database = strdup(strtok( NULL, "&" )); + //LogMsg( LG_SERVER, DEBUG, "DEBUG: Parameter Database is %s", RequestInfo->Database ); + Buffer = strtok( NULL, "=" ); + } + else if( strcmp(Buffer,"QueryString") == 0 ) + { + RequestInfo->QueryString = strdup(strtok( NULL, "&" )); + //LogMsg( LG_SERVER, DEBUG, "DEBUG: Parameter QueryString is %s", RequestInfo->QueryString ); + Buffer = strtok( NULL, "=" ); + } + else if( strcmp(Buffer,"Capability") == 0 ) + { + RequestInfo->Capability = strdup(strtok( NULL, "&\0" )); + //LogMsg( LG_SERVER, DEBUG, "DEBUG: Parameter Capability is %s", RequestInfo->Capability ); + Buffer = strtok( NULL, "=" ); + } + else if( strcmp(Buffer,"ClientID") == 0 ) + { + RequestInfo->ClientID = strdup(strtok( NULL, "&" )); + //LogMsg( LG_SERVER, DEBUG, "DEBUG: Parameter ClientID is %s", RequestInfo->ClientID ); + Buffer = strtok( NULL, "=" ); + } + else if( strcmp(Buffer,"Command") == 0 ) + { + RequestInfo->Command = atoi( strtok( NULL, "&" ) ); + //LogMsg( LG_SERVER, DEBUG, "DEBUG: Parameter Command is %i", RequestInfo->Command ); + Buffer = strtok( NULL, "=" ); + } + else if( strcmp(Buffer,"Endianess") == 0 ) + { + RequestInfo->Endianess = atoi( strtok( NULL, "&" ) ); + //LogMsg( LG_SERVER, DEBUG, "DEBUG: Parameter Endianess is %i", RequestInfo->Endianess ); + Buffer = strtok( NULL, "=" ); + } + else if( strcmp(Buffer,"NumberOfQueryParameters") == 0 ) + { + RequestInfo->NumberOfQueryParams = atoi( strtok( NULL, "&" ) ); + //LogMsg( LG_SERVER, DEBUG, "DEBUG: Parameter NumberOfQueryParams is %i", RequestInfo->NumberOfQueryParams ); + Buffer = strtok( NULL, "=" ); + } + else if( strcmp(Buffer,"BinDataSize") == 0 ) + { + RequestInfo->BinDataSize = atoi( strtok( NULL, "&" ) ); + //LogMsg( LG_SERVER, DEBUG, "DEBUG: Parameter BinDataSize is %i", RequestInfo->BinDataSize ); + Buffer = strtok( NULL, "=" ); + } + else if( strcmp(Buffer,"BinData") == 0 ) + { + // This parameter has to be the last one! + RequestInfo->BinData = (char*)mymalloc( RequestInfo->BinDataSize ); + memcpy(RequestInfo->BinData, Source + (SourceLen-RequestInfo->BinDataSize), RequestInfo->BinDataSize ); + //set Buffer to NULL => exit this while block + Buffer = NULL; + } + else if( strcmp(Buffer,"ClientType") == 0 ) + { + Buffer = strtok( NULL, "&" ); + //LogMsg( LG_SERVER, DEBUG, "DEBUG: Parameter Type is %s", Buffer ); + /* BROWSER? */ + if( strcmp(Buffer,"BROWSER") == 0 ) + RequestInfo->ClientType = 1; + /* Rasclient? */ + else if( strcmp(Buffer,"RASCLIENT") == 0 ) + RequestInfo->ClientType = 2; + /* Sonstiges */ + else + { + //LogMsg( LG_SERVER, DEBUG, "DEBUG: Unknown Parameter %s", Buffer ); + return REQU_UNKNOWN_CLIENT; + } + Buffer = strtok( NULL, "=" ); + } + else + return REQU_UNKNOWN_PARAMETER; + } + + free(Input); + return REQU_OK; +} + + +/******************************************************** + * + * Analyses the client request (using the "GetHTTPRequest" function) + * and executes the query (function "processQuery"). + * The result is written into the client response structure. + * + **********************************************************/ +void InterpretePOSTRequest ( struct ClientBase *Client ) +{ + int result = 0; + struct HTTPRequest RequestInfo; + char *tmp; + + /* Initialize RequestInfo */ + RequestInfo.Database = NULL; + RequestInfo.QueryString = NULL; + RequestInfo.ClientType = 0; + RequestInfo.ClientID = NULL; + RequestInfo.Command = 0; + RequestInfo.Endianess = 0; + RequestInfo.NumberOfQueryParams = 0; + RequestInfo.BinDataSize = 0; + RequestInfo.BinData = NULL; + RequestInfo.Capability = NULL; + + //LogMsg( LG_SERVER, DEBUG, "DEBUG: InterpretePostRequest() ..." ); + //LogMsg( LG_SERVER, DEBUG, "DEBUG: Request.BodySize is %d",Client->Request.BodySize ); + + /* get the necessary parameters */ + result = GetHTTPRequest( Client->Request.BodyBuff, Client->Request.BodySize, &RequestInfo ); + + if( result == REQU_OK ) + { + // here the query is actually executed + char* queryResult; + long resultLen; + ServerComm* scObject = ServerComm::actual_servercomm; + + // the cast should be save, function only called on HTTP requests + //LogMsg( LG_SERVER, DEBUG, "DEBUG: Vor processRequest ..." ); + resultLen = ((HttpServer*)scObject)->processRequest(1, RequestInfo.Database, RequestInfo.Command, + RequestInfo.QueryString, RequestInfo.BinDataSize, + RequestInfo.BinData, RequestInfo.Endianess, + queryResult,RequestInfo.Capability); + + //LogMsg( LG_SERVER, DEBUG, "DEBUG: ... nach processRequest." ); + + // Note that this has to be freed!!!!! + Client->Response.Body = queryResult; + Client->Response.BodySize = resultLen; + + + Client->ClientType = RequestInfo.ClientType; + //LogMsg( LG_SERVER, DEBUG, "DEBUG: ResponseBody is %s", Client->Response.Body ); + } + else + { + //LogMsg( LG_SERVER, DEBUG, "DEBUG: InterpretePostRequest(): send error response. " ); + Client->Response.Body = ComposeErrorResponse( result, RequestInfo.ClientType ); + Client->Response.BodySize = strlen( Client->Response.Body ); + Client->ClientType = RequestInfo.ClientType; + //LogMsg( LG_SERVER, DEBUG, "DEBUG: ResponseBody is %s", Client->Response.Body ); + } + + // free RequestInfo + free(RequestInfo.Database); + free(RequestInfo.QueryString); + free(RequestInfo.Capability); + free(RequestInfo.BinData); + free(RequestInfo.ClientID); + +} + + + +void CreateRasResponse( struct HTTPMode *Mode, struct ClientBase *Client ) +{ + char *Head; + char *Buff; + size_t BuffSize = BUFFSIZE; + char MDate[30]; + + + + //LogMsg( LG_SERVER, DEBUG, "DEBUG: Response Header begin, malloc BUFFSIZE is %d", BUFFSIZE ); + + HTTP_Date( MDate, 30 ); + + if( ( Head = (char*)mymalloc( BuffSize ) ) == NULL ) + { + ErrorMsg( E_SYS, ERROR, + "ERROR: CreateRasResponse() - malloc() error for head buffer." ); + } + + BuffSize -= 1; + Buff = Head; + bzero( Head, BuffSize ); + CreateStatusLine( Buff, &BuffSize, STATUS_OK, Mode->Protocol ); + + /* General Message Header */ + Buff = Head + strlen( Head ); + SNPrintf( Buff, &BuffSize, "Date: %s\r\n", MDate ); + Buff = Head + strlen( Head ); + SNPrintf( Buff, &BuffSize, "Connection: close\r\n" ); + + /* Response Message Header */ + Buff = Head + strlen( Head ); + SNPrintf( Buff, &BuffSize, SERVERFIELD ); + + /* Entity Message Header */ + Buff = Head + strlen( Head ); + switch( Client->ClientType ) + { + case BROWSER: + SNPrintf( Buff, &BuffSize, "Content-Type: text/html\r\n" ); + break; + case RASCLIENT: + SNPrintf( Buff, &BuffSize, "Content-Type: application/octet-stream\r\n" ); + break; + default: + SNPrintf( Buff, &BuffSize, "Content-Type: application/octet-stream\r\n" ); + break; + } + Buff = Head + strlen( Head ); + + //LogMsg( LG_SERVER, DEBUG, "DEBUG: ClientResponse BodySize is %d", Client->Response.BodySize ); + + SNPrintf( Buff, &BuffSize, "Content-Length: %d\r\n", Client->Response.BodySize ); + Buff = Head + strlen( Head ); + SNPrintf( Buff, &BuffSize, "Last-Modified: %s\r\n", MDate ); + Buff = Head + strlen( Head ); + SNPrintf( Buff, &BuffSize, "\r\n" ); + + Client->Response.Head = Head; + //LogMsg( LG_SERVER, DEBUG, "DEBUG: Response Header is %s", Client->Response.Head ); + +} + + + +void WriteAccessLog( struct ClientBase *Client ) +{ + char TimeString[ DATE_BUFFSIZE ]; + struct hostent *Host; + char *Hostname = NULL; + + bzero( TimeString, DATE_BUFFSIZE ); + + if( (int)Client->Host.IPAddress != -1 ) + { + Host = gethostbyaddr( (const char *)&Client->Host.IPAddress, + sizeof( Client->Host.IPAddress ), AF_INET ); + if( Host != NULL ) + Hostname = Host->h_name; + else + Hostname = Client->Host.IPAddrString; + } + else + Hostname = Client->Host.IPAddrString; + + LogDate( TimeString, DATE_BUFFSIZE ); + LogMsg( LG_ACCESS, INFO, "%s - - %s \"%s\" %d %d\n", + Hostname, + TimeString, + Client->Request.Line.Vanilla, + Client->RespStatus, + Client->Response.BodySize ); +} + + + + -- cgit