/* * 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 ); }