diff options
author | Constantin Jucovschi <cj@ubuntu.localdomain> | 2009-04-24 07:20:22 -0400 |
---|---|---|
committer | Constantin Jucovschi <cj@ubuntu.localdomain> | 2009-04-24 07:20:22 -0400 |
commit | 8f27e65bddd7d4b8515ce620fb485fdd78fcdf89 (patch) | |
tree | bd328a4dd4f92d32202241b5e3a7f36177792c5f /httpserver | |
download | rasdaman-upstream-8f27e65bddd7d4b8515ce620fb485fdd78fcdf89.tar.gz rasdaman-upstream-8f27e65bddd7d4b8515ce620fb485fdd78fcdf89.tar.xz rasdaman-upstream-8f27e65bddd7d4b8515ce620fb485fdd78fcdf89.zip |
Initial commitv8.0
Diffstat (limited to 'httpserver')
28 files changed, 7197 insertions, 0 deletions
diff --git a/httpserver/Makefile.am b/httpserver/Makefile.am new file mode 100644 index 0000000..94d883b --- /dev/null +++ b/httpserver/Makefile.am @@ -0,0 +1,33 @@ +# 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 <http://www.gnu.org/licenses/>. +# +# Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +# rasdaman GmbH. +# +# For more information please see <http://www.rasdaman.org> +# or contact Peter Baumann via <baumann@rasdaman.com>. +# +# MAKEFILE FOR: +# Rasdaman http server +# +################################################################## + +noinst_LIBRARIES=libhttpserver.a +AM_CFLAGS=$(CXXFLAGS) +libhttpserver_a_SOURCES= types.h http.h server.h defs.h http-defs.h protos.h \ + logging.cc signals.cc config.cc support.cc childs.cc init.cc \ + http-support.cc http-methods.cc http-fields.cc http-date.cc \ + http-error.cc http-readmsg.cc http-writemsg.cc \ + http-doit.cc http.cc httpserver.h main.cc diff --git a/httpserver/childs.cc b/httpserver/childs.cc new file mode 100644 index 0000000..66729f1 --- /dev/null +++ b/httpserver/childs.cc @@ -0,0 +1,246 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ + +#include "mymalloc/mymalloc.h" +/*------------------------------------------------------------------------*/ +/* childs.c - child process handling. */ +/* */ +/*------------------------------------------------------------------------*/ +/* Comments: */ +/* - intended purpose: */ +/* Keep all functions related to child processes in one place. */ +/* - Status: */ +/* - Functions to Create and Initialize a child is thrown in. */ +/* - Should think over the actual design - looks somewhat */ +/* messy now. It should be something more clean and of */ +/* generic use, so it can be reused in future projects. */ +/* - Nevertheless -- it works, and there should be no creeping */ +/* surprises in the code... [FLW Alert... ;-)] */ +/*------------------------------------------------------------------------*/ +/* + * RCS: + * $RCSfile: childs.c,v $ $Revision: 1.6 $ $State: Exp $ + * $Locker: $ + */ + + +#include "defs.h" +#include "protos.h" +#include "types.h" +#include "server.h" +#include "http-defs.h" +#include "http.h" + +extern struct ServerBase Server; + + +/****** childs/InitChild ***************************************************** +* +* NAME +* InitChild -- data initializition after forking the child process. +* +* SYNOPSIS +* int InitChild( struct ClientBase *Client ); +* +* FUNCTION +* +* RESULT +* +* NOTES +* +* BUGS +* +* SEE ALSO +* +****************************************************************************** +* +*/ + +rc_t InitChild( struct ClientBase *Client ) +{ + char *UnknownIP = "0.0.0.0"; + + /* Init ClientBase */ + strcpy( Client->Host.IPAddrString, inet_ntoa( Client->Socket.sin_addr ) ); + if( Client->Host.IPAddrString == NULL ) + strcpy( Client->Host.IPAddrString, UnknownIP ); + Client->Host.IPAddress = inet_addr( Client->Host.IPAddrString ); + + Client->Comm.ConnStatus = CONN_UNDEFINED; + InitHTTPMsg( &Client->Response ); + InitReqInfo( &Client->Request ); + + return( OK ); +} + + + +/****** childs/NewChild ****************************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* ### Temporary fork() wrapper. +* Will be later expanded into something more usefull... +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +pid_t NewChild( struct ChildBase *List, struct FDsets *PDSets, struct ClientBase *Client ) +{ + struct ChildBase *Child; + + Child = (struct ChildBase*)mymalloc( sizeof( struct ChildBase ) ); + if( Child == NULL ) // Failure? => QUIT. + ErrorMsg( E_SYS, FAIL, "FAIL: NewChild(): malloc() failed!" ); + + // Open pipe + if( pipe( Child->PD ) < 0 ) + { + ErrorMsg( E_SYS, ERROR, "ERROR: NewChild(): pipe() failed." ); + Child->PipeStatus = PIPE_CLOSED; + } + else + { + Child->PipeStatus = PIPE_OPEN; + } + + Child->PId = fork(); + + if( Child->PId < 0 ) // fork() Error? => QUIT. + ErrorMsg( E_SYS, FAIL, "FAIL: NewChild(): fork() failed!" ); + + if( Child->PId > 0 ) // parent process? + { + // Add Pipe-Descriptors to PipeSets + if( Child->PipeStatus == PIPE_OPEN ) + { + FD_SET( Child->PD[0], &PDSets->Read ); + if( Child->PD[0] > PDSets->MaxFD ) + PDSets->MaxFD = Child->PD[0]; + close( Child->PD[1] ); + } + // Add Child to List + AddChild( List, Child ); + } + else // child process + { + InitChild( Client ); + // Remember PD to parent + Client->Pipe = Child->PD[1]; + close( Child->PD[0] ); + free( Child ); + } + + return( Child->PId ); +} + + +void CleanupChild( struct ChildBase *List, struct FDsets *PDSets, pid_t PId ) +{ + struct ChildBase *Ptr; + struct ChildBase *Tmp; + + LogMsg( LG_SERVER, DEBUG, "DEBUG: Begin CleanupChild ..." ); + + if(PDSets == NULL) + LogMsg( LG_SERVER, DEBUG, "DEBUG: ########### PDSets is NULL!" ); + if(PId == (pid_t)0) + LogMsg( LG_SERVER, DEBUG, "DEBUG: ########### ChildPid is NULL!" ); + if(List == NULL) + LogMsg( LG_SERVER, DEBUG, "DEBUG: ########### ChildBase is NULL!" ); + + if( List->PId > 0 ) + { + for( Ptr = List; Ptr->next != List; Ptr = Ptr->next ) + { + if( Ptr->next->PId == PId ) + { + Tmp = Ptr->next; + break; + } + } + // Close pipe + close( Tmp->PD[0] ); + // Remove Pipe-Descriptors from PipeSets + FD_CLR( Tmp->PD[0], &PDSets->Read ); + RemChild( List, Tmp ); + } +} + + +void AddChild( struct ChildBase *List, struct ChildBase *Child ) +{ + Child->next = List->next; + Child->prev = List; + List->next->prev = Child; + List->next = Child; + List->PId++; +} + + +void RemChild( struct ChildBase *List, struct ChildBase *Child ) +{ + Child->prev->next = Child->next; + Child->next->prev = Child->prev; + List->PId--; + free( Child ); +} + + +struct ChildBase *GetChild( struct ChildBase *List, pid_t PId ) +{ + struct ChildBase *Ptr; + + if( List->PId > 0 ) + { + for( Ptr = List; Ptr->next != List; Ptr = Ptr->next ) + { + if( Ptr->next->PId == PId ) + return( Ptr->next ); + } + return( NULL ); + } + else + return( NULL ); +} diff --git a/httpserver/config.cc b/httpserver/config.cc new file mode 100644 index 0000000..d4c235b --- /dev/null +++ b/httpserver/config.cc @@ -0,0 +1,465 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ +#include "mymalloc/mymalloc.h" +/*------------------------------------------------------------------------*/ +/* config.c - configuration and setup stuff. */ +/*------------------------------------------------------------------------*/ +/* Comments: */ +/* - intended purpose: */ +/* Contains all code related to setup the configuration. */ +/* - Status: */ +/* - There were more features planned than are now available. */ +/* - Especially some arguments are disabled or non-functional, */ +/* so: onle use "-c <config-filepath>", and don't care about */ +/* the rest! */ +/* - The Flag Handling of Verbose/Debug-Mode is somewhat broken */ +/* - ReadConfig() uses static buffers. But they are protected */ +/* against overwriting. */ +/* - ReadConfig() contains some printf()'s. */ +/* - Handling of Sub-Server configuration still disabled. */ +/* - The rest should be okay. */ +/*------------------------------------------------------------------------*/ + +#include <iostream> +using namespace std; + +#include "defs.h" +#include "protos.h" +#include "server.h" +#include <unistd.h> // for gethostname() + +#ifdef SOLARIS + // function prototype with C linkage + extern "C" int gethostname(char *name, int namelen); +#endif + +extern int globalHTTPPort; + +/****** config/ReadArgs ****************************************************** +* +* NAME +* ReadArgs -- +* +* SYNOPSIS +* int ReadArgs( int argc, char *argv[] ); +* +* FUNCTION +* +* RESULT +* +* NOTES +* +* BUGS +* +* SEE ALSO +* +****************************************************************************** +* +*/ + +rc_t ReadArgs( struct ServerBase *Server, int argc, char *argv[] ) +{ + int i; + int LogMode = LM_VERBOSE; + char *Filename = NULL; + + for( i = 1; i < argc; ++i) + { + if( argv[i][0] != '-' ) + { + break; + } + switch( argv[i][1] ) + { + case 'D': /* Debug-Flag setzen */ + LogMode |= LF_STDERR; + break; + case 'V': /* Verbose-Flag setzen */ + LogMode |= LF_VERB; + break; + case 'c': /* Config-File im naechsten Argument */ + if( ++i >= argc ) /* Keine weiteren Argumente? */ + { + printf( "Oops: Missing config filename after '-c' switch.\n" ); + printf( "Please try again.\n" ); + Exit( FAIL ); + } + else + { + Filename = argv[i]; + } + break; + case 'h': + printf( "Usage: %s [-c <ConfigFile>] [-V] [-D]\n", argv[0] ); + Exit( WARN ); + break; + case '?': + printf( "Usage: %s [-c <ConfigFile>] [-V] [-D]\n", argv[0] ); + Exit( WARN ); + break; + default: /* Kein bekanntes Argument */ + printf( "Unknown option '%s'.\n", argv[i] ); + Exit( FAIL ); + break; + } + } + + return( CheckAndSet( Server, Filename, LogMode ) ); +} + + +/****** config/CheckAndSet *************************************************** +* +* NAME +* CheckAndSet -- +* +* SYNOPSIS +* int CheckAndSet( char *Directory, char *Filename, int LogMode ); +* +* FUNCTION +* +* RESULT +* +* NOTES +* +* BUGS +* +* SEE ALSO +* +****************************************************************************** +* +*/ + +rc_t CheckAndSet( struct ServerBase *Server, char *Filename, int LogMode ) +{ + rc_t RC = 0; + + /* we skipped the config file + char *Def_ConfFile = "httpserver.conf"; + + if( Filename == NULL ) + { + LogMsg( LG_SERVER, WARN, "WARN: No config file given - using default %s.", + Def_ConfFile ); + Filename = Def_ConfFile; + } + + SetFilename( Server, FT_CONFFILE, Filename ); + */ + Server->Log.Mode = LogMode; + + return( RC ); +} + + +/****** config/SetServDir **************************************************** +* +* NAME +* SetServDir -- +* +* SYNOPSIS +* int SetServDir( char *Dirname ); +* +* FUNCTION +* +* RESULT +* +* NOTES +* +* BUGS +* +* SEE ALSO +* +****************************************************************************** +* +*/ + +rc_t SetServDir( struct ServerBase *Server, char *Dirname ) +{ + char *Buffer; + size_t BuffSize; + size_t MaxPath; + + if ( ( Buffer = PathAlloc( &BuffSize ) ) == NULL ) + ErrorMsg( E_SYS, FAIL, "FAIL: Buffer allocation for pathname of server directory." ); + Buffer[0] = '\0'; + + /* Platz lassen, um auch noch Filenamen + '\0' im Buffer */ + /* unterbringen zu können. */ + + MaxPath = BuffSize - strlen( "httpserver.conf" ); + if( strlen( Dirname ) >= MaxPath ) + ErrorMsg( E_SYS, FAIL, "FAIL: Pathname of server directory too long." ); + Buffer = strncpy( Buffer, Dirname, MaxPath ); + + if( Buffer[ strlen( Buffer ) - 1 ] != '/' ) + Buffer = strcat( Buffer, "/" ); + + if(Server->Directory) free(Server->Directory); + Server->Directory = Buffer; + return( OK ); +} + + +/****** config/SetFilename *************************************************** +* +* NAME +* SetConfFile -- +* +* SYNOPSIS +* int SetConfFile( char *Filename ); +* +* FUNCTION +* +* RESULT +* +* NOTES +* +* BUGS +* +* SEE ALSO +* +****************************************************************************** +* +*/ + + +rc_t SetFilename( struct ServerBase *Server, int Type, char *Filename ) +{ + char *Buffer = NULL; + size_t BuffSize = 0; + size_t PathLength, FileLength; + + if( Filename != NULL ) + { + if ( ( Buffer = PathAlloc( &BuffSize ) ) == NULL ) + ErrorMsg( E_SYS, FAIL, "FAIL: Buffer allocation for filename \"%s\".", Filename ); + Buffer[0] = '\0'; + + if( Server->Directory != NULL ) + PathLength = strlen( Server->Directory ); + FileLength = strlen( Filename ); + + if( FileLength >= BuffSize ) + ErrorMsg( E_SYS, FAIL, "FAIL: Pathname of file \"%s\" too long.", Filename ); + + if( Filename[0] != '/' ) + { + if( ( PathLength + FileLength ) >= BuffSize ) + ErrorMsg( E_SYS, FAIL, "FAIL: Path/filename of file \"%s\" too long.", Filename ); + + Buffer = strncpy( Buffer, Server->Directory, BuffSize ); + } + Buffer = strncat( Buffer, Filename, FileLength ); + } + + switch( Type ) + { + case FT_CONFFILE: + Server->ConfigFile = Buffer; + break; + case FT_ACCESSLOG: + Server->Log.Access.Filename = Buffer; + break; + case FT_SERVERLOG: + Server->Log.Server.Filename = Buffer; + break; + case FT_COMMLOG: + Server->Log.Comm.Filename = Buffer; + break; + case FT_PIDFILE: + Server->PidFile = Buffer; + break; + default: + ErrorMsg( E_SYS, ERROR, "ERROR: Unknown filetype: %d.", Type ); + return( ERROR ); + break; + } + return( OK ); +} + + +rc_t SetString( struct ServerBase *Server, int Type, char *String ) +{ + char *Buffer = NULL; + size_t BuffSize = 0; + + if( String != NULL ) + { + BuffSize = strlen( String ); + if ( ( Buffer = (char*)mymalloc( BuffSize+1 ) ) == NULL ) + ErrorMsg( E_SYS, FAIL, "FAIL: Buffer allocation for string \"%s\".", String ); + strcpy( Buffer, String ); + + switch( Type ) + { + case ST_HOSTNAME: + Server->Host.Name = Buffer; + break; + case ST_MAILADDRESS: + Server->AdminMailAddress = Buffer; + break; + default: + ErrorMsg( E_SYS, ERROR, "ERROR: Unknown stringtype: %d.", Type ); + return( ERROR ); + break; + } + return( OK ); + } + else + return( ERROR ); +} + + +/****** config/ConfigureServer *********************************************** +* +* NAME +* ConfigureServer -- +* +* SYNOPSIS +* int ConfigureServer( char *Keyword, char *Value1, char *Value2, +* char *Value3 ); +* +* FUNCTION +* +* RESULT +* +* NOTES +* +* BUGS +* +* SEE ALSO +* +****************************************************************************** +* +*/ + +rc_t ConfigureServer( struct ServerBase *Server ) +{ + char* myName; + char* rmanhome = CONFDIR; + int MaxURLLength = 120; + int BuffSize; + char* Buffer; + + BuffSize = strlen( rmanhome ); + + if ( ( Buffer = (char*)mymalloc( BuffSize+200 ) ) == NULL ) + ErrorMsg( E_SYS, FAIL, "FAIL: Buffer allocation for Server Configuration" ); + if( gethostname( Buffer, 200 ) ) + ErrorMsg( E_SYS, FAIL, "FAIL: Could not determine my Hostname" ); + + myName=strdup(Buffer); + + strcpy(Buffer,rmanhome); + SetFilename( Server, FT_COMMLOG, strcat(Buffer,"/httpcomm.log") ); + + Server->Port = globalHTTPPort; + + SetString( Server, ST_MAILADDRESS, "admin@localhost" ); + + SetString( Server, ST_HOSTNAME, myName ); + + SetServDir( Server, rmanhome ); + + strcpy(Buffer,rmanhome); + SetFilename( Server, FT_ACCESSLOG, strcat(Buffer,"/httpaccess.log") ); + + Server->MaxURLLength = MaxURLLength; + + strcpy(Buffer,rmanhome); + SetFilename( Server, FT_PIDFILE, strcat(Buffer,"/httpserver.pid") ); + + strcpy(Buffer,rmanhome); + SetFilename( Server, FT_SERVERLOG, strcat(Buffer,"/httpserver.log") ); + + free(Buffer); + free(myName); + + return( OK ); +} + + +/****** config/GetConfigKey ************************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* With Help from K&R, Chapter 6, binsearch(). +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +int GetConfigKey( char *Keyword ) +{ + struct KeywordKey KeyTable[] = + { + { "AccessLog", KEY_ACCESSLOG }, + { "CommLog", KEY_COMMLOG }, + { "IndexFile", KEY_INDEXFILE }, + { "MaxURLLength", KEY_MAXURLLENGTH }, + { "PidFile", KEY_PIDFILE }, + { "Port", KEY_PORT }, + { "ServerAdmin", KEY_SERVERADMIN }, + { "ServerLog", KEY_SERVERLOG }, + { "ServerName", KEY_SERVERNAME }, + { "ServerRoot", KEY_SERVERROOT }, + }; + + int cond; + int low; + int high; + + low = 0; + high = NUM_KEYS - 1; + + while( low < high ) + { + if( ( cond = strcmp( Keyword, KeyTable[ low ].Keyword ) ) == 0 ) + return( KeyTable[ low ].Key ); + low++; + } + + return -1; +} + diff --git a/httpserver/defs.h b/httpserver/defs.h new file mode 100644 index 0000000..26d3b36 --- /dev/null +++ b/httpserver/defs.h @@ -0,0 +1,249 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ +/*------------------------------------------------------------------------*/ +/* defs.h - Global defines and typedefs. */ +/*------------------------------------------------------------------------*/ + + +#ifndef _DEFS_H +#define _DEFS_H + + +/* Name of this daemon + */ + +#define PROGRAM_NAME "httpserver" +#define PROGRAM_VERSION "1.0" +#define DAEMONNAME PROGRAM_NAME "/" PROGRAM_VERSION +#define SERVERFIELD "Server: " DAEMONNAME "\r\n" + + +/* These constants *may* be changed + */ + +#define BUFFSIZE 8192 +#define MAXLINELEN 4096 +#define PIPE_BUFFSIZE 4096 +#define DATE_BUFFSIZE 40 +#define IO_BUFFSIZE 65536 +#define DEFAULT_MAXURLLENGTH 1000000 +// #define IO_BUFFSIZE 1024 + +/* TimeOut values are given in seconds + */ + + /* Timeout when talking with subservers and client */ +#define DIALOG_TIMEOUT 30 + /* Timeout when trying to get an unspecified mesage body, for example */ + /* dynamically generated output (typically CGI output). */ + /* You may have to *really* wait for it, so don't make it too small! */ +#define MSGBODY_TIMEOUT 30 + + +/* Misc. defines + */ + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#ifdef SOCKLENNOTDEFINED +typedef int socklen_t; +#endif + +#define max(a,b) (a > b ? a : b) +#define min(a,b) (a < b ? a : b) + +#define FOREGROUND 0 +#define BACKGROUND 1 + +/* Std.-Zugriffsberechtigung für neue Dateien */ +#define FILE_MODE ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ) + +/* Filetypes: */ +#define FT_NOFILE 0 +#define FT_CONFFILE 1 +#define FT_ACCESSLOG 2 +#define FT_SERVERLOG 3 +#define FT_COMMLOG 4 +#define FT_PIDFILE 5 +#define FT_CACHEFILE 6 + +/* Stringtypes: */ +#define ST_NOSTRING 0 +#define ST_HOSTNAME 1 +#define ST_MAILADDRESS 2 + + +/* Pipe Status: + */ + +#define PIPE_OPEN 1 +#define PIPE_CLOSED 0 + +/* LogFile Status: + */ + +#define FILE_OPEN 1 +#define FILE_CLOSED 0 + +/* Logging Modes: + * FILE (0) ServerLog und AccessLog einrichten und oeffnen + * STDIO (1) ServerLog -> stderr; AccessLog -> stdout + * DEBUG (2) Detaillierte Meldungen + */ + +#define LF_VERB 0x0001 +#define LF_STDERR 0x0002 + +#define LM_NORMAL 0 /* Not Verbose, write into log files */ +#define LM_VERBOSE 1 /* Verbose, write into log files */ +#define LM_STDERR 2 /* Not Verbose, use STDOUT/STDERR */ +#define LM_VERBOSE_STDERR 3 /* Verbose, use STDOUT/STDERR */ + + +/* LogLevels / ReturnCodes: + * DEBUG (1) Detaillierte Aktionsmeldungen + * INFO (2) Statusmeldungen + * NOTE (3) Hinweise auf besondere Situationen + * WARN (5) Behebbare Fehlersituationen + * ERROR (10) Fehler die zu Aktionsabbruch fuehren + * FAIL (20) Fehler die zum Programmabbruch fuehren + */ + +#define OK 0 +#define DEBUG 1 +#define INFO 2 +#define NOTE 3 +#define WARN 5 +#define ERROR 10 +#define FAIL 20 +#define DUMP 50 + +#define SYS_ERROR -1 + +/* Logging sub-systems: + * LG_SERVER generic server logfile + * LG_ACCESS HTTP request logfile + */ + +#define LG_SERVER 1 +#define LG_ACCESS 2 +#define LG_COMM 3 + +/* Error-Type flags: + * SYS TRUE System Error: strerror(errno) + * PRIV FALSE Application Error + */ + +#define E_SYS TRUE +#define E_PRIV FALSE + +/* Configuration Keyword Keys: + */ + +#define KEY_ACCESSLOG 1 +#define KEY_COMMLOG 2 +#define KEY_INDEXFILE 3 +#define KEY_MAXURLLENGTH 4 +#define KEY_PIDFILE 5 +#define KEY_PORT 6 +#define KEY_SERVERADMIN 7 +#define KEY_SERVERLOG 8 +#define KEY_SERVERNAME 9 +#define KEY_SERVERROOT 10 +#define NUM_KEYS 11 + +/* SubServer Communication Status Codes: + */ + +#define COMM_UNSUPPORTED -3 /* don't know how to handle comm state */ +#define COMM_UNEXPECTED -2 /* unexpected data received */ +#define COMM_FAILED -1 /* trouble while communicating, protocol failure */ +#define COMM_IDLE 0 /* not connected and ready */ +#define COMM_CONNECTING 1 /* trying to connect or "Keep-Alive" connection */ +#define COMM_GET_RESPHEAD 2 /* reading and processing response header */ +#define COMM_GET_RESPBODY 3 /* reading response body */ +#define COMM_MAY_GET_BODY 4 /* have to check for response body */ +#define COMM_HERE_IS_MORE 5 /* select() indicated that there IS more data */ +#define COMM_DONE 9 /* communication done, protocol OK */ +/* Currently not in use: */ +#define COMM_RECONNECTING 10 /* trying to reconnect for authorization */ +#define COMM_VERIFYING 11 /* analysing response: add. processing required */ +#define COMM_SENDINGAUTH 12 /* (re-)sending request with authorization */ + +/* Connection Mode Codes: + */ + +#define CONN_FAILURE -3 /* Failure when setting up connection */ +#define CONN_BROKEN -2 /* Connection broken while communicating */ +#define CONN_UNDEFINED -1 /* Connection not initiated */ +#define CONN_CLOSE 0 /* Connection will be closed ASAP */ +#define CONN_OPEN 1 /* Connection is ready for communication */ +#define CONN_ERROR 2 /* Error condition in communication */ + +/* ToDo Action Codes: + */ + +#define DO_NOTHING 0 +#define DO_SINGLE_SERVER 1 +#define DO_REWRITE 2 +#define DO_SEND_RESPONSE 3 +#define DO_SEND_ERROR 4 +#define DO_SHUTDOWN 5 + +/* Client Types */ +#define BROWSER 1 +#define RASCLIENT 2 + +/* ToDo Argument Codes: + */ + +/* - DO_REWRITE: */ +#define MODE_HTTP_1_0 1 +#define MODE_HTTP_1_1 2 + +/* - INTERPRETE_POST_REQUEST: - */ +#define REQU_OK 1 +#define REQU_UNKNOWN_PARAMETER 2 +#define REQU_UNKNOWN_CLIENT 3 + +/* - Result Types: Error, MDD, Skalar - */ +#define RESULT_ERROR 1 +#define RESULT_MDD 2 +#define RESULT_SKALAR 3 + +/* - DO_SEND_ERROR: */ +/* -> use HTTP Status Codes (see http-defs.h) */ + +/* - DO_SHUTDOWN: */ +#define CLOSE_ALL 1 +#define CLOSE_CLIENT_ONLY 2 /* Currently not used */ + +/* - DO_NOTHING: */ +#define REALLY_NOTHING 0 + +#endif /* _DEFS_H not defined */ diff --git a/httpserver/http-date.cc b/httpserver/http-date.cc new file mode 100644 index 0000000..82324dd --- /dev/null +++ b/httpserver/http-date.cc @@ -0,0 +1,97 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ +/*------------------------------------------------------------------------*/ +/* http-support.c - get and set HTTP date strings. */ +/* */ +/*------------------------------------------------------------------------*/ +/* + * RCS: + * $RCSfile: http-date.c,v $ $Revision: 1.1 $ $State: Exp $ + * $Locker: $ + */ + + +#include "defs.h" +#include "types.h" +#include "protos.h" +#include "server.h" +#include "http-defs.h" +#include "http.h" + + +/****** http-date/HTTP_Date ************************************************** +* +* NAME +* HTTP_Date -- create a HTTP date string. +* +* SYNOPSIS +* rc_t HTTP_Date( char *Buffer, size_t BuffSize ); +* +* FUNCTION +* Creates a date (and time) string as defined in the HTTP +* specifications. It uses the `gmtime()' system function to get the +* current time in Greenwich Mean Time and creates a date string with +* the help of `strftime()'. +* +* INPUTS +* Buffer - a pointer to a buffer for the new string. +* BuffSize - size of the Buffer for the date string. +* +* RESULT +* Returns the "OK" status code if it succesfully created the date +* string, "ERROR" otherwise. The only reason for this function should +* fail, is when the Buffer is not large enough. +* As a side effect, if the function succeeds, Buffer will contain the +* current time and date in a format conforming to the HTTP +* specifications. +* +* BUGS +* Does not check if Buffer is NULL-pointer. +* +* SEE ALSO +* RFC 2068: Hypertext Transfer Protocol -- HTTP/1.1 +* +****************************************************************************** +* +*/ + +rc_t HTTP_Date( char *Buffer, size_t BuffSize ) +{ + time_t systime; + time_t *time_ptr; + struct tm *tm_ptr; + size_t strsize; + + time_ptr = &systime; + time( time_ptr ); + + /* HTTP Date: "wdy, dd mmm yyyy hh:mm:ss GMT" */ + + tm_ptr = gmtime( time_ptr ); + strsize = strftime( Buffer, BuffSize, "%a, %d %b %Y %H:%M:%S GMT", tm_ptr ); + if( strsize == BuffSize ) + return( ERROR ); + else + return( OK ); +} + diff --git a/httpserver/http-defs.h b/httpserver/http-defs.h new file mode 100644 index 0000000..12df7bc --- /dev/null +++ b/httpserver/http-defs.h @@ -0,0 +1,214 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ +/*------------------------------------------------------------------------*/ +/* http-defs.h - defines for HTTP. */ +/*------------------------------------------------------------------------*/ + + +#ifndef _HTTP_DEFS_H +#define _HTTP_DEFS_H + +#define AUTH_MAX_RETRIES 1 + +/* + */ + +#define HTTP_REQUEST 1 +#define HTTP_RESPONSE 2 + + +/* ReqInfo/RespInfo structure states when reading/parsing requests/responses + */ + +#define RI_EMPTY 0 +#define RI_READ_OK 1 +#define RI_READ_ERROR 2 +#define RI_PARSE_OK 3 +#define RI_PARSE_WARN 4 /* trouble when parsing MsgHeaders */ +#define RI_PARSE_ERROR 5 /* Request Line/Status Line not found */ + +/* Known Protocols: + */ + +#define HTTP_0_9 0 +#define HTTP_0_9_STRING "" +#define HTTP_1_0 1 +#define HTTP_1_0_STRING "HTTP/1.0" +#define HTTP_1_1 2 +#define HTTP_1_1_STRING "HTTP/1.1" + + +/* HTTP Method Keys: + */ + +#define MKEY_NONE 0 +#define MKEY_GET 1 +#define MKEY_HEAD 2 +#define MKEY_PUT 3 +#define MKEY_POST 4 +#define MKEY_DELETE 5 +#define MKEY_OPTIONS 6 +#define MKEY_TRACE 7 +#define MKEY_UNKNOWN 999 + +#define NUM_MKEYS 7 + + +/* HTTP Status Codes: + */ + +#define STATUS_UNKNOWN 0 +#define STATUS_UNDEFINED 0 +#define STATUS_Continue 100 +#define STATUS_Switching_Protocols 101 +#define STATUS_OK 200 +#define STATUS_Created 201 +#define STATUS_Accepted 202 +#define STATUS_Non_Authoritative_Information 203 +#define STATUS_No_Content 204 +#define STATUS_Reset_Content 205 +#define STATUS_Partial_Content 206 +#define STATUS_Multiple_Choices 300 +#define STATUS_Moved_Permanently 301 +#define STATUS_Moved_Temporarily 302 +#define STATUS_See_Other 303 +#define STATUS_Not_Modified 304 +#define STATUS_Use_Proxy 305 +#define STATUS_Switch_Proxy 306 +#define STATUS_Bad_Request 400 +#define STATUS_Unauthorized 401 +#define STATUS_Payment_Required 402 +#define STATUS_Forbidden 403 +#define STATUS_Not_Found 404 +#define STATUS_Method_Not_Allowed 405 +#define STATUS_Not_Acceptable 406 +#define STATUS_Proxy_Authentication_Required 407 +#define STATUS_Request_Timeout 408 +#define STATUS_Conflict 409 +#define STATUS_Gone 410 +#define STATUS_Length_Required 411 +#define STATUS_Precondition_Failed 412 +#define STATUS_Request_Entity_Too_Large 413 +#define STATUS_Request_URI_Too_Long 414 +#define STATUS_Unsupported_Media_Type 415 +#define STATUS_Requested_Range_Not_Valid 416 +#define STATUS_Expectation_Failed 419 +#define STATUS_Internal_Server_Error 500 +#define STATUS_Not_Implemented 501 +#define STATUS_Bad_Gateway 502 +#define STATUS_Service_Unavailable 503 +#define STATUS_Gateway_Timeout 504 +#define STATUS_HTTP_Version_Not_Supported 505 +#define STATUS_Redirection_Failed 506 + + +/* HTTP Header Keys: + */ + +#define HKEY_CONTINUE 0 +#define HKEY_Accept 1 +#define HKEY_Accept_Charset 2 +#define HKEY_Accept_Encoding 3 +#define HKEY_Accept_Language 4 +#define HKEY_Accept_Ranges 5 +#define HKEY_Age 6 +#define HKEY_Allow 7 +#define HKEY_Authorization 8 +#define HKEY_Cache_Control 9 +#define HKEY_Compliance 10 +#define HKEY_Connection 11 +#define HKEY_Content_Base 12 +#define HKEY_Content_Encoding 13 +#define HKEY_Content_Language 14 +#define HKEY_Content_Length 15 +#define HKEY_Content_Location 16 +#define HKEY_Content_MD5 17 +#define HKEY_Content_Range 18 +#define HKEY_Content_Type 19 +#define HKEY_Date 20 +#define HKEY_ETag 21 +#define HKEY_Expect 22 +#define HKEY_Expires 23 +#define HKEY_From 24 +#define HKEY_Host 25 +#define HKEY_If_Modified_Since 26 +#define HKEY_If_Match 27 +#define HKEY_If_None_Match 28 +#define HKEY_If_Range 29 +#define HKEY_If_Unmodified_Since 30 +#define HKEY_Keep_Alive 31 +#define HKEY_Last_Modified 32 +#define HKEY_Location 33 +#define HKEY_Max_Forwards 34 +#define HKEY_Non_Compliance 35 +#define HKEY_Pragma 36 +#define HKEY_Proxy_Authenticate 37 +#define HKEY_Proxy_Authorization 38 +#define HKEY_Public 39 +#define HKEY_Range 40 +#define HKEY_Referer 41 +#define HKEY_Retry_After 42 +#define HKEY_Server 43 +#define HKEY_Set_Proxy 44 +#define HKEY_Transfer_Encoding 45 +#define HKEY_Upgrade 46 +#define HKEY_User_Agent 47 +#define HKEY_Vary 48 +#define HKEY_Warning 49 +#define HKEY_WWW_Authenticate 50 +#define HKEY_UNKNOWN 999 + +#define NUM_HKEYS 50 + + +/* Language Keys: + */ + + /* These numbers are intentionally choosen: */ + /* Space left for upgrades. */ + /* See also: ISP 639 / Version 1 */ + +#define LANG_UNKNOWN 0 +#define LANG_DE 22 +#define LANG_EN 25 +#define LANG_EO 26 +#define LANG_ES 27 +#define LANG_FR 34 +#define LANG_IT 53 + + +/* Security Realm Keys: + */ + +#define REALM_ERROR -1 +#define REALM_UNKNOWN 0 +#define REALM_UNDEFINED 0 +#define REALM_IPCLASS_A 1 +#define REALM_IPCLASS_B 2 +#define REALM_IPCLASS_C 3 +#define REALM_IPADDRESS 4 +#define REALM_HOSTNAME 10 +#define REALM_DOMAIN 11 + + +#endif /* _HTTP_DEFS_H not defined */ 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ + +#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, "<BR><H1>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 ); +} + + + + diff --git a/httpserver/http-error.cc b/httpserver/http-error.cc new file mode 100644 index 0000000..92fd451 --- /dev/null +++ b/httpserver/http-error.cc @@ -0,0 +1,446 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ + +#include "mymalloc/mymalloc.h" +/*------------------------------------------------------------------------*/ +/* http-error.c - HTTP Error message handling. */ +/*------------------------------------------------------------------------*/ +/* Comments: */ +/* - Status: */ +/* - The internal response strings should be completed and */ +/* the overly long lines should be broken up. */ +/* - A somewhat more dynamic approach to the handling of the */ +/* error string table should be used. */ +/*------------------------------------------------------------------------*/ + +#include "defs.h" +#include "protos.h" +#include "server.h" +#include "http-defs.h" +#include "http.h" + +struct HTTPError HTTPErrorTable[] = +{ + { + STATUS_Continue, + "Continue", + "", + "" + }, + { + STATUS_Switching_Protocols, + "Switching Protocols", + "", + "" + }, + { + STATUS_OK, + "OK", + "", + "" + }, + { + STATUS_Created, + "Created", + "", + "" + }, + { + STATUS_Accepted, + "Accepted", + "", + "" + }, + { + STATUS_Non_Authoritative_Information, + "Non-Authoritative Information", + "", + "" + }, + { + STATUS_No_Content, + "No Content", + "", + "" + }, + { + STATUS_Reset_Content, + "Reset Content", + "", + "" + }, + { + STATUS_Partial_Content, + "Partial Content", + "", + "" + }, + { + STATUS_Multiple_Choices, + "Multiple Choices", + "", + "" + }, + { + STATUS_Moved_Permanently, + "Moved Permanently", + "", + "" + }, + { + STATUS_Moved_Temporarily, + "Moved Temporarily", + "", + "" + }, + { + STATUS_See_Other, + "See Other", + "", + "" + }, + { + STATUS_Not_Modified, + "Not Modified", + "", + "" + }, + { + STATUS_Use_Proxy, + "Use Proxy", + "", + "" + }, + { + STATUS_Bad_Request, + "Bad Request", + "<HTML><HEAD>\n<TITLE>400 Bad Request</TITLE>\n</HEAD>"\ + "<BODY>\n<H1>Bad Request</H1>\n"\ + "Your browser sent a request that this server could not understand.\n"\ + "</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>400 Bad Request</TITLE>\n</HEAD>"\ + "<BODY>\n<H1>Problem mit Request</H1>\n"\ + "Ihr Browser hat einen Request geschickt, der von diesem Server "\ + "nicht verstanden wurde.\n"\ + "</BODY></HTML>\n" + }, + { + STATUS_Unauthorized, + "Unauthorized", + "<HTML><HEAD>\n<TITLE>401 Unauthorized</TITLE>\n</HEAD>"\ + "<BODY>\n<H1>Unauthorized</H1>\n"\ + "You have not the necessary rights to get the requested document.\n"\ + "</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>401 Unauthorized</TITLE>\n</HEAD>"\ + "<BODY>\n<H1>Nicht Authorisiert</H1>\n"\ + "Sie haben nicht die notwendigen Rechte um auf das angeforderte "\ + "Dokument zuzugreifen.\n"\ + "</BODY></HTML>\n" + }, + { + STATUS_Payment_Required, + "Payment Required", + "<HTML><HEAD>\n<TITLE>402 Payment Required</TITLE>\n</HEAD><BODY>\n<H1>Payment Required</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>402 Payment Required</TITLE>\n</HEAD><BODY>\n<H1>Payment Required</H1>\n</BODY></HTML>\n" + }, + { + STATUS_Forbidden, + "Forbidden", + "<HTML><HEAD>\n<TITLE>403 Forbidden</TITLE>\n</HEAD><BODY>\n<H1>Forbidden</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>403 Forbidden</TITLE>\n</HEAD><BODY>\n<H1>Forbidden</H1>\n</BODY></HTML>\n" + }, + { + STATUS_Not_Found, + "Not Found", + "<HTML><HEAD>\n<TITLE>404 Not Found</TITLE>\n</HEAD>"\ + "<BODY>\n<H1>Not Found</H1>\n"\ + "<EM>Tried hard, really...</EM>\n"\ + "</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>404 Not Found</TITLE>\n</HEAD>"\ + "<BODY>\n<H1>Nicht Gefunden</H1>\n"\ + "Das angeforderte Dokument wurde nicht gefunden.\n"\ + "</BODY></HTML>\n" + }, + { + STATUS_Method_Not_Allowed, + "Method Not Allowed", + "<HTML><HEAD>\n<TITLE>405 Method Not Allowed</TITLE>\n</HEAD><BODY>\n<H1>Method Not Allowed</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>405 Method Not Allowed</TITLE>\n</HEAD><BODY>\n<H1>Method Not Allowed</H1>\n</BODY></HTML>\n" + }, + { + STATUS_Not_Acceptable, + "Not Acceptable", + "<HTML><HEAD>\n<TITLE>406 Not Acceptable</TITLE>\n</HEAD><BODY>\n<H1>Not Acceptable</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>406 Not Acceptable</TITLE>\n</HEAD><BODY>\n<H1>Not Acceptable</H1>\n</BODY></HTML>\n" + }, + { + STATUS_Proxy_Authentication_Required, + "Proxy Authentication Required", + "<HTML><HEAD>\n<TITLE>407 Proxy Authentication Required</TITLE>\n</HEAD><BODY>\n<H1>Proxy Authentication Required</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>407 Proxy Authentication Required</TITLE>\n</HEAD><BODY>\n<H1>Proxy Authentication Required</H1>\n</BODY></HTML>\n" + }, + { + STATUS_Request_Timeout, + "Request Timeout", + "<HTML><HEAD>\n<TITLE>408 Request Timeout</TITLE>\n</HEAD><BODY>\n<H1>Request Timeout</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>408 Request Timeout</TITLE>\n</HEAD><BODY>\n<H1>Request Timeout</H1>\n</BODY></HTML>\n" + }, + { + STATUS_Conflict, + "Conflict", + "<HTML><HEAD>\n<TITLE>409 Conflict</TITLE>\n</HEAD><BODY>\n<H1>Conflict</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>409 Conflict</TITLE>\n</HEAD><BODY>\n<H1>Conflict</H1>\n</BODY></HTML>\n" + }, + { + STATUS_Gone, + "Gone", + "<HTML><HEAD>\n<TITLE>410 Gone</TITLE>\n</HEAD><BODY>\n<H1>Gone</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>410 Gone</TITLE>\n</HEAD><BODY>\n<H1>Gone</H1>\n</BODY></HTML>\n" + }, + { + STATUS_Length_Required, + "Length Required", + "<HTML><HEAD>\n<TITLE>411 Length Required</TITLE>\n</HEAD><BODY>\n<H1>Length Required</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>411 Length Required</TITLE>\n</HEAD><BODY>\n<H1>Length Required</H1>\n</BODY></HTML>\n" + }, + { + STATUS_Precondition_Failed, + "Precondition Failed", + "<HTML><HEAD>\n<TITLE>412 Precondition Failed</TITLE>\n</HEAD><BODY>\n<H1>Precondition Failed</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>412 Precondition Failed</TITLE>\n</HEAD><BODY>\n<H1>Precondition Failed</H1>\n</BODY></HTML>\n" + }, + { + STATUS_Request_Entity_Too_Large, + "Request Entity Too Large", + "<HTML><HEAD>\n<TITLE>413 Request Entity Too Large</TITLE>\n</HEAD><BODY><H1>Request Entity Too Large\n</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>413 Request Entity Too Large</TITLE>\n</HEAD><BODY><H1>Request Entity Too Large\n</H1>\n</BODY></HTML>\n" + }, + { + STATUS_Request_URI_Too_Long, + "Request-URI Too Long", + "<HTML><HEAD>\n<TITLE>414 Request-URI Too Long</TITLE>\n</HEAD><BODY><H1>Request-URI Too Long\n</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>414 Request-URI Too Long</TITLE>\n</HEAD><BODY><H1>Request-URI Too Long\n</H1>\n</BODY></HTML>\n" + }, + { + STATUS_Unsupported_Media_Type, + "Unsupported Media Type", + "<HTML><HEAD>\n<TITLE>415 Unsupported Media Type</TITLE>\n</HEAD><BODY>\n<H1>Unsupported Media Type</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>415 Unsupported Media Type</TITLE>\n</HEAD><BODY>\n<H1>Unsupported Media Type</H1>\n</BODY></HTML>\n" + }, + { + STATUS_Requested_Range_Not_Valid, + "Requested Range Not Valid", + "<HTML><HEAD>\n<TITLE>416 Requested Range Not Valid</TITLE>\n</HEAD><BODY>\n<H1>Requested Range Not Valid</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>416 Requested Range Not Valid</TITLE>\n</HEAD><BODY>\n<H1>Requested Range Not Valid</H1>\n</BODY></HTML>\n" + }, + { + STATUS_Internal_Server_Error, + "Internal Server Error", + "<HTML><HEAD>\n<TITLE>500 Internal Server Error</TITLE>\n</HEAD><BODY>\n<H1>Internal Server Error</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>500 Internal Server Error</TITLE>\n</HEAD><BODY>\n<H1>Internal Server Error</H1>\n</BODY></HTML>\n" + }, + { + STATUS_Not_Implemented, + "Not Implemented", + "<HTML><HEAD>\n<TITLE>501 Not Implemented</TITLE>\n</HEAD><BODY>\n<H1>Not Implemented</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>501 Not Implemented</TITLE>\n</HEAD><BODY>\n<H1>Not Implemented</H1>\n</BODY></HTML>\n" + }, + { + STATUS_Bad_Gateway, + "Bad Gateway", + "<HTML><HEAD>\n<TITLE>502 Bad Gateway</TITLE>\n</HEAD><BODY>\n<H1>Bad Gateway</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>502 Bad Gateway</TITLE>\n</HEAD><BODY>\n<H1>Bad Gateway</H1>\n</BODY></HTML>\n" + }, + { + STATUS_Service_Unavailable, + "Service Unavailable", + "<HTML><HEAD>\n<TITLE>503 Service Unavailable</TITLE>\n</HEAD><BODY>\n<H1>Service Unavailable</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>503 Service Unavailable</TITLE>\n</HEAD><BODY>\n<H1>Service Unavailable</H1>\n</BODY></HTML>\n" + }, + { + STATUS_Gateway_Timeout, + "Gateway Timeout", + "<HTML><HEAD>\n<TITLE>504 Gateway Timeout</TITLE>\n</HEAD><BODY>\n<H1>Gateway Timeout</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>504 Gateway Timeout</TITLE>\n</HEAD><BODY>\n<H1>Gateway Timeout</H1>\n</BODY></HTML>\n" + }, + { + STATUS_HTTP_Version_Not_Supported, + "HTTP Version Not Supported", + "<HTML><HEAD>\n<TITLE>505 HTTP Version Not Supported</TITLE>\n</HEAD><BODY>\n<H1>HTTP Version Not Supported</H1>\n</BODY></HTML>\n", + "<HTML><HEAD>\n<TITLE>505 HTTP Version Not Supported</TITLE>\n</HEAD><BODY>\n<H1>HTTP Version Not Supported</H1>\n</BODY></HTML>\n" + }, +}; + +#define NUM_STATUSCODES 37 + + +/****** http-error/GetHTTPErrorTableEntry ************************************ +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +int GetHTTPErrorTableEntry( int Code ) +{ + int low; + int high; + int mid; + int check[ NUM_STATUSCODES ]; + int i; + + low = 0; + high = NUM_STATUSCODES - 1; + for( i = 0; i < NUM_STATUSCODES; i++ ) + check[ i ] = 0; + + while( low <= high ) + { + mid = ( low + high ) / 2; + if( Code < HTTPErrorTable[mid].Code ) + { + if( check[mid] == 0 ) + { + check[mid] = 1; + high = mid - 1; + } + else + return( 0 ); + } + else if( Code > HTTPErrorTable[mid].Code ) + { + if( check[mid] == 0 ) + { + check[mid] = 1; + low = mid + 1; + } + else + return( 0 ); + } + else + { + return( mid ); + } + } +} + + +/****** http-error/CreateHTTPError ******************************************* +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +rc_t CreateHTTPError( int Code, struct HTTPMode *Mode, struct HTTPMsg *Msg ) +{ + char *Head; + char *Buff; + size_t BuffSize = BUFFSIZE; + char *Body; + size_t BodySize; + size_t Used; + + int Entry; + char Date[DATE_BUFFSIZE]; + + bzero( Date, DATE_BUFFSIZE ); + if( ( Head = (char*)mymalloc( BuffSize ) ) == NULL ) + { + ErrorMsg( E_SYS, ERROR, + "ERROR: CreateHTTPError() - malloc() error for head buffer." ); + return( ERROR ); + } + Buff = Head; + BuffSize -= 1; + + HTTP_Date( Date, DATE_BUFFSIZE ); + Entry = GetHTTPErrorTableEntry( Code ); + Body = HTTPErrorTable[Entry].Message_en; + BodySize = strlen( HTTPErrorTable[Entry].Message_en ); + + CreateStatusLine( Buff, &BuffSize, Code, Mode->Protocol ); + Used = strlen( Head ); + Buff = Head + Used; + BuffSize -= Used; + SNPrintf( Buff, &BuffSize, + "Date: %s\r\n" + "Connection: close\r\n" + "%s" + "Content-Type: text/html\r\n" + "Content-Language: %s\r\n" + "Content-Length: %d\r\n\r\n", + Date, SERVERFIELD, "en", BodySize ); + + Msg->Head = Head; + Msg->Body = strdup(Body); + Msg->BodySize = BodySize; + + return( OK ); +} diff --git a/httpserver/http-fields.cc b/httpserver/http-fields.cc new file mode 100644 index 0000000..76f00f7 --- /dev/null +++ b/httpserver/http-fields.cc @@ -0,0 +1,218 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ +/*------------------------------------------------------------------------*/ +/* http-fields.c - get and set HTTP Header fieldnames. */ +/*------------------------------------------------------------------------*/ + + +#include "defs.h" +#include "protos.h" +#include "server.h" +#include "http-defs.h" +#include "http.h" + + +struct KeywordKey FieldnameKeyTable[] = +{ + { "Accept" , HKEY_Accept }, + { "Accept-Charset" , HKEY_Accept_Charset }, + { "Accept-Encoding" , HKEY_Accept_Encoding }, + { "Accept-Language" , HKEY_Accept_Language }, + { "Accept-Ranges" , HKEY_Accept_Ranges }, + { "Accept-charset" , HKEY_Accept_Charset }, + { "Accept-encoding" , HKEY_Accept_Encoding }, + { "Accept-language" , HKEY_Accept_Language }, + { "Accept-ranges" , HKEY_Accept_Ranges }, + { "Age" , HKEY_Age }, + { "Allow" , HKEY_Allow }, + { "Authorization" , HKEY_Authorization }, + { "Cache-Control" , HKEY_Cache_Control }, + { "Cache-control" , HKEY_Cache_Control }, + { "Compliance" , HKEY_Compliance }, + { "Connection" , HKEY_Connection }, + { "Content-Base" , HKEY_Content_Base }, + { "Content-Encoding" , HKEY_Content_Encoding }, + { "Content-Language" , HKEY_Content_Language }, + { "Content-Length" , HKEY_Content_Length }, + { "Content-Location" , HKEY_Content_Location }, + { "Content-MD5" , HKEY_Content_MD5 }, + { "Content-Range" , HKEY_Content_Range }, + { "Content-Type" , HKEY_Content_Type }, + { "Content-base" , HKEY_Content_Base }, + { "Content-encoding" , HKEY_Content_Encoding }, + { "Content-language" , HKEY_Content_Language }, + { "Content-length" , HKEY_Content_Length }, + { "Content-location" , HKEY_Content_Location }, + { "Content-range" , HKEY_Content_Range }, + { "Content-type" , HKEY_Content_Type }, + { "Date" , HKEY_Date }, + { "ETag" , HKEY_ETag }, + { "Expect" , HKEY_Expect }, + { "Expires" , HKEY_Expires }, + { "From" , HKEY_From }, + { "Host" , HKEY_Host }, + { "If-Modified-Since" , HKEY_If_Modified_Since }, + { "If-Match" , HKEY_If_Match }, + { "If-None-Match" , HKEY_If_None_Match }, + { "If-Range" , HKEY_If_Range }, + { "If-Unmodified-Since" , HKEY_If_Unmodified_Since }, + { "Keep-Alive" , HKEY_Keep_Alive }, + { "Last-Modified" , HKEY_Last_Modified }, + { "Location" , HKEY_Location }, + { "Max-Forwards" , HKEY_Max_Forwards }, + { "Non-Compliance" , HKEY_Non_Compliance }, + { "Pragma" , HKEY_Pragma }, + { "Proxy-Authenticate" , HKEY_Proxy_Authenticate }, + { "Proxy-Authorization" , HKEY_Proxy_Authorization }, + { "Public" , HKEY_Public }, + { "Range" , HKEY_Range }, + { "Referer" , HKEY_Referer }, + { "Retry-After" , HKEY_Retry_After }, + { "Server" , HKEY_Server }, + { "Set-Proxy" , HKEY_Set_Proxy }, + { "Transfer-Encoding" , HKEY_Transfer_Encoding }, + { "Upgrade" , HKEY_Upgrade }, + { "User-Agent" , HKEY_User_Agent }, + { "Vary" , HKEY_Vary }, + { "WWW-Authenticate" , HKEY_WWW_Authenticate }, + { "Warning" , HKEY_Warning }, +}; + +#define NUM_FIELDS 62 + + +/****** http-fields/HTTP_GetHKey ********************************************* +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +int HTTP_GetHKey( char *Keyword ) +{ + int cond; + int low; + int high; + int mid; + int check[ NUM_FIELDS ]; + int i; + + low = 0; + high = NUM_FIELDS - 1; + for( i = 0; i < NUM_FIELDS; i++ ) + check[ i ] = 0; + + while( low <= high ) + { + mid = ( low + high ) / 2; + if( ( cond = strcmp( Keyword, FieldnameKeyTable[mid].Keyword ) ) < 0 ) + { + if( check[mid] == 0 ) + { + check[mid] = 1; + high = mid - 1; + } + else + return( HKEY_UNKNOWN ); + } + else if( cond > 0 ) + { + if( check[mid] == 0 ) + { + check[mid] = 1; + low = mid + 1; + } + else + return( HKEY_UNKNOWN ); + } + else + return( FieldnameKeyTable[mid].Key ); + } + return( HKEY_UNKNOWN ); +} + + +/****** http-fields/HTTP_GetFieldName **************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +char *HTTP_GetFieldName( int Key ) +{ + int i; + + for( i = 0; i < NUM_FIELDS; i++ ) + { + if( FieldnameKeyTable[i].Key == Key ) + return( FieldnameKeyTable[i].Keyword ); + } + return( NULL ); +} diff --git a/httpserver/http-methods.cc b/httpserver/http-methods.cc new file mode 100644 index 0000000..4a99f71 --- /dev/null +++ b/httpserver/http-methods.cc @@ -0,0 +1,163 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ +/*------------------------------------------------------------------------*/ +/* http-methods.c - get and set HTTP Method names. */ +/*------------------------------------------------------------------------*/ + + +#include "defs.h" +#include "protos.h" +#include "server.h" +#include "http-defs.h" +#include "http.h" + +#include <ctype.h> + +struct KeywordKey MethodKeyTable[] = +{ + { "GET", MKEY_GET }, + { "HEAD", MKEY_HEAD }, + { "PUT", MKEY_PUT }, + { "POST", MKEY_POST }, + { "DELETE", MKEY_DELETE }, + { "OPTIONS", MKEY_OPTIONS }, + { "TRACE", MKEY_TRACE }, +}; + + +/****** http-methods/HTTP_GetMKey ******************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +int HTTP_GetMKey( char *Keyword ) +{ + int cond; + int low; + int high; + int mid; + int check[ NUM_MKEYS ]; + int i; + + low = 0; + high = NUM_MKEYS - 1; + for( i = 0; i < NUM_MKEYS; i++ ) + check[ i ] = 0; + if( !isalpha( *Keyword ) ) + return( MKEY_NONE ); + while( low <= high ) + { + mid = ( low + high ) / 2; + if( ( cond = strcmp( Keyword, MethodKeyTable[mid].Keyword ) ) < 0 ) + { + if( check[mid] == 0 ) + { + check[mid] = 1; + high = mid - 1; + } + else + return( MKEY_UNKNOWN ); + } + else if( cond > 0 ) + { + if( check[mid] == 0 ) + { + check[mid] = 1; + low = mid + 1; + } + else + return( MKEY_UNKNOWN ); + } + else + return( MethodKeyTable[mid].Key ); + } + return( MKEY_UNKNOWN ); +} + + +/****** http-methods/HTTP_GetMethodName ************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +char *HTTP_GetMethodName( int Key ) +{ + int i; + + for( i = 0; i < NUM_MKEYS; i++ ) + { + if( MethodKeyTable[i].Key == Key ) + return( MethodKeyTable[i].Keyword ); + } + return( NULL ); +} diff --git a/httpserver/http-readmsg.cc b/httpserver/http-readmsg.cc new file mode 100644 index 0000000..da88274 --- /dev/null +++ b/httpserver/http-readmsg.cc @@ -0,0 +1,1014 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ + +#include "mymalloc/mymalloc.h" +/*------------------------------------------------------------------------*/ +/* http-readmsg.c - generic HTTP-Message functions: */ +/* Reading and parsing of HTTP-Messages. */ +/*------------------------------------------------------------------------*/ +/* Comments: */ +/* - Status: */ +/* - Missing symbolic constant BUFFBLOCK_SIZE. */ +/* - To check: All Buffers free()ed? Also in error cases? */ +/* - ParseReqLine()/ParseRespLine() use static buffers, but */ +/* they are protected against overwriting. */ +/*------------------------------------------------------------------------*/ + + +#include "defs.h" +#include "protos.h" +#include "server.h" +#include "http-defs.h" +#include "http.h" + + +/****** http-readmsg/ReadHeader ********************************************** +* +* NAME +* ReadHeader -- Read a HTTP message header from a socket. +* +* SYNOPSIS +* char *ReadHeader( int SockFD, size_t *size ); +* +* FUNCTION +* This function reads from the socket descriptor "SockFD" until it +* detects an empty line (two <CR><LF> sequences in a row). Every read +* character is stored in a buffer, which is dynamically allocated. +* If the empty line is detected, it returns a pointer to the buffer +* and the variable "*size" contains the buffersize. +* +* INPUTS +* SockFD - The socket descriptor to read from. +* size - A pointer to a variable which may hold the size of the buffer +* which contains the message header. +* +* RESULT +* A pointer a buffer with the HTTP message header. +* +* NOTES +* The function checks for errors returned by the "read()" system call. +* Other problems ("out-of-band" data, socket errors) are not checked. +* +****************************************************************************** +* +*/ + +int ReadHeader( int SockFD, char **Buffer, size_t *BuffSize ) +{ + size_t BuffBlock = 1024; + size_t minbuff = 2; + size_t nread = 0; + size_t sumread = 0; + char *Ptr; + int check_eol = 0; + int error = FALSE; + int elapsed = 0; + + for(;;) + { + if( *BuffSize - sumread < minbuff ) + { + *BuffSize += BuffBlock; + *Buffer = (char*)realloc( *Buffer, *BuffSize ); + if( *Buffer == NULL ) + { + ErrorMsg( E_SYS, ERROR, "ERROR: ReadHeader(): malloc() failed." ); + error = TRUE; + break; + } + bzero( *Buffer, *BuffSize ); + } + + Ptr = *Buffer + sumread; + nread = read( SockFD, Ptr, 1 ); + if( nread == 1 ) + { + elapsed = 0; /* Clear timeout counter */ + sumread++; + if( *Ptr == '\n' ) /* Check for two consecutive "\r\n" */ + { + if( check_eol == 1 ) + break; /* if found => End-Of-Header */ + else + check_eol = 1; + } + else if( *Ptr != '\r' ) + check_eol = 0; + } + else if( nread == 0 ) + { + if( elapsed >= 30 ) + { + ErrorMsg( E_PRIV, ERROR, "ERROR: ReadHeader() timed out." ); + error = TRUE; + break; + } + sleep(1); + elapsed++; + } + else + { + if( errno != EAGAIN ) + { + ErrorMsg( E_SYS, ERROR, "ERROR: ReadHeader(): read() failed." ); + error = TRUE; + break; + } + else + { + sleep( elapsed ); + if( elapsed >= 30 ) + { + ErrorMsg( E_PRIV, ERROR, "ERROR: ReadHeader() timed out." ); + error = TRUE; + break; + } + sleep(1); + elapsed++; + } + } + } + if( error == TRUE ) + { + if( *Buffer != NULL ) + free( *Buffer ); + *Buffer = NULL; + *BuffSize = 0; + return( RI_READ_ERROR ); + } + else + return( RI_READ_OK ); +} + + +/****** http-readmsg/ReadBody ************************************************ +* +* NAME +* ReadBody -- Read a HTTP message body from a socket. +* +* SYNOPSIS +* char *ReadBody( int SockFD, size_t BuffSize ); +* +* FUNCTION +* This functions reads the HTTP message body with the size "BuffSize" +* from the socket "SockFD". A buffer for the message body is allocated +* within this function and a pointer to this buffer is returned. +* +* INPUTS +* SockFD - The socket descriptor to read from. +* BuffSize - The size of the message body. +* +* RESULT +* A pointer to the buffer which contains the message body. +* +* NOTES +* The function checks for errors returned by the "read()" system call. +* Other problems ("out-of-band" data, socket errors) are not checked. +* +****************************************************************************** +* +*/ + +char *ReadBody( int SockFD, size_t BuffSize ) +{ + int nread = 0; + char *Buffer = NULL; + + if( ( Buffer = (char*)mymalloc( BuffSize + 1 ) ) == NULL ) + { + ErrorMsg( E_SYS, ERROR, "ERROR: malloc error for HTTP-Body buffer." ); + return( Buffer ); + } + bzero( Buffer, BuffSize + 1); + nread = ReadN( SockFD, Buffer, BuffSize ); + if( nread < 0 ) + { + ErrorMsg( E_SYS, ERROR, "ERROR: ReadBody(): read() failed." ); + free( Buffer ); + Buffer = NULL; + return( Buffer ); + } + else if( nread < BuffSize ) + { + ErrorMsg( E_PRIV, WARN, "WARN: MessageBody not of expected size." ); + } + return( Buffer ); +} + + +/****** http-readmsg/ParseReqHeader ****************************************** +* +* NAME +* ParseReqHeader - Parse the HTTP request header. +* +* SYNOPSIS +* rc_t ParseReqHeader( struct ReqInfo *Request ); +* +* FUNCTION +* This is the main function for parsing the HTTP request headers. +* It first calls "ParseReqLine()" to actually parse the "Request +* Line" (the first line of an HTTP request) and then calls +* subsequently "ParseMsgLine()" for every non-empty line following +* the request-line. +* +* The results of this parsing process are stored in a data structure +* of the type "ReqInfo", which is later used for searching in and +* reconstructing of the header informations. +* +* INPUTS +* Request - A pointer to a data structure of the type ReqInfo. +* +* RESULT +* Returns "OK" if everything went well. +* +* BUGS +* It doesn't check if "Request" is a NULL pointer. +* +* SEE ALSO +* ParseRespHeader(), ParseReqLine(), ParseMsgLine(). +* +****************************************************************************** +* +*/ + +rc_t ParseReqHeader( struct ReqInfo *Request ) +{ + char *Buffer; + char *Param; + int Key; + struct MsgHeader *Header; + + Header = NULL; + Buffer = Request->HeadBuff; + if( Request->State == RI_READ_OK ) + { + Buffer = ParseReqLine( Buffer, Request ); + //LogMsg( LG_SERVER, DEBUG, "DEBUG: *** Parsed Req %s", Buffer ); + if( Request->State == RI_PARSE_ERROR ) + return( WARN ); + } + else + { + Request->State = RI_PARSE_ERROR; + return( WARN ); + } + + while( Buffer != NULL && *Buffer != '\0' ) + { + Key = -1; + Param = NULL; + Buffer = ParseMsgLine( Buffer, &Key, &Param ); + //LogMsg( LG_SERVER, DEBUG, "DEBUG: *** Parsed Msg %s", Buffer ); + if( Buffer != NULL ) + { + Header = AppendMsgHeader( Request->Last, Key, Param ); + Request->Last = Header; + } + if( Request->First == NULL ) + Request->First = Header; + } + if( Request->First == NULL ) + { + Request->State = RI_PARSE_WARN; + return( WARN ); + } + else + { + Request->State = RI_PARSE_OK; + return( OK ); + } +} + + +/****** http-readmsg/ParseRespHeader ***************************************** +* +* NAME +* ParseRespHeader - Parse the HTTP response header. +* +* SYNOPSIS +* rc_t ParseRespHeader( struct RespInfo *Response ); +* +* FUNCTION +* This is the main function for parsing the HTTP response headers. +* It first calls "ParseRespLine()" to actually parse the "Response +* Line" (the first line of an HTTP response) and then calls +* subsequently "ParseMsgLine()" for every non-empty line following +* the request-line. +* +* The results of this parsing process are stored in a data structure +* of the type "RespInfo", which is later used for searching in and +* reconstructing of the header informations. +* +* INPUTS +* Response - A pointer to a data structure of the type RespInfo. +* +* RESULT +* Returns "OK" if everything went well. +* +* BUGS +* Actually, it always returns an "OK" status code. +* It doesn't check if "Response" is a NULL pointer. +* +* SEE ALSO +* ParseReqHeader(), ParseRespLine(), ParseMsgLine(). +* +****************************************************************************** +* +*/ + +rc_t ParseRespHeader( struct RespInfo *Response ) +{ + char *Buffer; + char *Keyword; + char *Param; + size_t i; + int Key; + struct MsgHeader *Header; + + Header = NULL; + Buffer = Response->HeadBuff; + Buffer = ParseRespLine( Buffer, Response ); + + while( Buffer != NULL ) + { + Key = -1; + Param = NULL; + Buffer = ParseMsgLine( Buffer, &Key, &Param ); + if( Buffer != NULL ) + { + Header = AppendMsgHeader( Response->Last, Key, Param ); + Response->Last = Header; + } + if( Response->First == NULL ) + Response->First = Header; + } + return( OK ); +} + + +/****** http-readmsg/ParseReqLine ******************************************** +* +* NAME +* ParseReqLine - Parse the HTTP request line. +* +* SYNOPSIS +* char *ParseReqLine( char *Buffer, struct ReqInfo *Request ); +* +* FUNCTION +* This function parses the beginning of the buffer "Buffer" for the +* contents of a HTTP request line. Since the parsing process (as +* implemented here) is destructive to the original string, the buffer +* is first scanned for a <CR><LF> sequence (end-of-line mark of a +* HTTP message) while copying any other character encountered to a +* secondary buffer. This "Backup" of the request line is later used +* for logging purposes, so it can be referred to in case of a problem. +* +* Then the request line is parsed and broken up in its components, as +* they are: Request method, URL, protocol-id and version. These are +* stored in the appropriate places in a "ReqInfo" data structure. +* +* INPUTS +* Buffer - A pointer to the raw data of a HTTP request. +* Request - A pointer to a data structure of the type ReqInfo. +* +* RESULT +* A pointer into the buffer, pointing to the rest of the data to be +* processed. +* +* BUGS +* "Buffer" and "Request" are not checked for NULL pointers. +* The "Backup" of the request line has a maximum length of +* "MAXLINELEN". +* +* SEE ALSO +* ParseRespLine(). +* +****************************************************************************** +* +*/ + +char *ParseReqLine( char *Buffer, struct ReqInfo *Request ) +{ + char Tmp[ MAXLINELEN ]; + char *NewBuffer; + char *Ptr; + char *Keyword; + char *URL = NULL; + char *PVersion = NULL; + int Key = 0; + int PVmaj = -1; + int PVmin = -1; + int i; + int eol_found = FALSE; + + Keyword = Buffer; + + for( Ptr = Buffer, i = 0; i < MAXLINELEN; Ptr++, i++ ) + { + if( *Ptr == '\r') + Tmp[i] = '\0'; + else if( *Ptr == '\n' ) + { + Tmp[i] = '\0'; + eol_found = TRUE; + break; + } + else + Tmp[i] = *Ptr; + } + Tmp[i+1] = '\0'; + if( eol_found != TRUE ) + { + Request->State = RI_PARSE_ERROR; + return( NULL ); + } + + for( Ptr = Buffer; *Ptr != '\n'; Ptr++ ) + { + if( *Ptr == ' ' ) + { + if( URL == NULL ) + { + *Ptr = '\0'; + URL = ++Ptr; + } + else + { + *Ptr = '\0'; + ++Ptr; + if( strcmp( "HTTP/", Ptr ) ) + { + Ptr += 5; + PVersion = Ptr; + } + } + } + else if( *Ptr == '\r' ) + { + *Ptr = '\0'; + } + } + *Ptr = '\0'; + NewBuffer = ++Ptr; + + if( PVersion != NULL ) + { + for( Ptr = PVersion; *Ptr != '\0'; Ptr++ ) + { + if( *Ptr == '.' ) + { + *Ptr = '\0'; + ++Ptr; + break; + } + } + PVmaj = strtol( PVersion, NULL, 10 ); + PVmin = strtol( Ptr, NULL, 10 ); + } + + if( URL != NULL ) + { + Request->Line.Vanilla = (char*)mymalloc( strlen( Tmp ) + 1 ); + Request->Line.Method = HTTP_GetMKey( Keyword ); + // SplitURL( URL, &Request->Line.URL ); + Request->Line.URL.Path = URL; + Request->Line.Version.Major = PVmaj; + Request->Line.Version.Minor = PVmin; + strcpy( Request->Line.Vanilla, Tmp ); + Request->State = RI_PARSE_OK; + return( NewBuffer ); + } + else + { + Request->State = RI_PARSE_ERROR; + return( NULL ); + } +} + + +/****** http-readmsg/ParseRespLine ******************************************* +* +* NAME +* ParseRespLine - Parse the HTTP response line. +* +* SYNOPSIS +* char *ParseRespLine( char *Buffer, struct RespInfo *Response ); +* +* FUNCTION +* This function parses the beginning of the buffer "Buffer" for the +* contents of a HTTP response line. Since the parsing process (as +* implemented here) is destructive to the original string, the buffer +* is first scanned for a <CR><LF> sequence (end-of-line mark of a +* HTTP message) while copying any other character encountered to a +* secondary buffer. This "Backup" of the response line is later used +* for logging purposes, so it can be referred to in case of a problem. +* +* Then the response line is parsed and broken up in its components, as +* they are: Protocol-id and version, (numeric) status code and +* "reason phrase". These are stored in the appropriate places in a +* "RespInfo" data structure. +* +* INPUTS +* Buffer - A pointer to the raw data of a HTTP request. +* Response - A pointer to a data structure of the type RespInfo. +* +* RESULT +* A pointer into the buffer, pointing to the rest of the data to be +* processed. +* +* BUGS +* "Buffer" and "Response" are not checked for NULL pointers. +* The "Backup" of the response line has a maximum length of +* "MAXLINELEN". +* +* SEE ALSO +* ParseReqLine(). +* +****************************************************************************** +* +*/ + +char *ParseRespLine( char *Buffer, struct RespInfo *Response ) +{ + char Tmp[ MAXLINELEN ]; + char *Ptr; + char *Reason = NULL; + char *PVersion = NULL; + char *Status = NULL; + char *PVmaj = NULL; + char *PVmin = NULL; + int i; + + for( Ptr = Buffer, i = 0; i <= MAXLINELEN - 1; Ptr++, i++ ) + { + if( *Ptr == '\r') + Tmp[i] = '\0'; + else if( *Ptr == '\n' ) + { + Tmp[i] = '\0'; + break; + } + else + Tmp[i] = *Ptr; + } + Tmp[i+1] = '\0'; + + Response->Line.Vanilla = (char*)mymalloc( strlen( Tmp ) + 1 ); + strcpy( Response->Line.Vanilla, Tmp ); + + if( strcmp( "HTTP/", Buffer ) ) + { + PVmaj = Buffer + 5; + for( Ptr = PVmaj; *Ptr != ' '; Ptr++ ) + { + if( *Ptr == '.' ) + { + *Ptr = '\0'; + PVmin = ++Ptr; + } + } + *Ptr = '\0'; + Response->Line.Version.Major = strtol( PVmaj, NULL, 10 ); + Response->Line.Version.Minor = strtol( PVmin, NULL, 10 ); + } + else + return( NULL ); + + Status = ++Ptr; + while( *Ptr != ' ' ) + Ptr++; + *Ptr = '\0'; + Response->Line.Status = strtol( Status, NULL, 10 ); + + Ptr++; + Response->Line.Reason = Ptr; + + while( *Ptr != '\n' ) + { + if( *Ptr == '\r' ) + *Ptr = '\0'; + Ptr++; + } + *Ptr = '\0'; + + return( ++Ptr ); +} + + +/****** http-readmsg/ParseMsgLine ******************************************** +* +* NAME +* ParseMsgLine - Parse a HTTP message header line. +* +* SYNOPSIS +* char *ParseMsgLine( char *Buffer, int *Key, char **Param ); +* +* FUNCTION +* This function parses a HTTP message header field into the +* components field name and field content ("value" of this field). +* The appropriate token for the field name is stored in the variable +* where "Key" points to and "Param" points to the field content +* string. +* +* INPUTS +* Buffer - A pointer to the raw data of a HTTP request. +* Key - A pointer to a variable which will store the field name token. +* Param - A pointer to a character pointer, which will refer to +* the field content string. +* +* RESULT +* A pointer into the buffer, pointing to the rest of the data to be +* processed. +* +* NOTES +* In case of unknown field names, the field content string will +* contain not only the value of the field, instead it will contain the +* complete header line. +* +* BUGS +* "Buffer", "Key" and "Param" are not checked against NULL pointers. +* +****************************************************************************** +* +*/ + +char *ParseMsgLine( char *Buffer, int *Key, char **Param ) +{ + char *Keyword; + char *Ptr; + int HaveKey = FALSE; + + if( ( *Buffer == ' ' ) || ( *Buffer == '\t' ) ) + { + *Key = HKEY_CONTINUE; + *Param = Buffer; + + for( Ptr = Buffer; *Ptr != '\n'; Ptr++ ) + { + if( *Ptr == '\r' ) + *Ptr = '\0'; + else if( *Ptr == '\0' ) + break; + } + *Ptr = '\0'; + return( ++Ptr ); + } + + Keyword = Buffer; + for( Ptr = Buffer; *Ptr != '\n'; Ptr++ ) + { + if( *Ptr == ':' ) + { + if( HaveKey == FALSE ) + { + HaveKey = TRUE; + *Ptr = '\0'; + *Key = HTTP_GetHKey( Keyword ); + if( *Key != HKEY_UNKNOWN ) + { + Ptr++; + while( ( *Ptr == ' ' ) || ( *Ptr == '\t' ) ) + ++Ptr; + *Param = Ptr; + } + else + { + *Ptr = ':'; + *Param = Buffer; + } + } + } + else if( *Ptr == '\r' ) + *Ptr = '\0'; + else if( *Ptr == '\0' ) + break; + } + *Ptr = '\0'; + + if( *Key == -1 || HaveKey == FALSE ) + return( NULL ); + else + return( ++Ptr ); +} + + +/****** http-readmsg/splitURL ************************************************ +* +* NAME +* splitURL - break up an URL string into its components. +* +* SYNOPSIS +* void splitURL( char *Buffer, struct URLComps *URL ); +* +* FUNCTION +* This function searches in a linked list of "MsgHeader" elements +* for one with a "Content_Length" token, and returns the integer +* value of it. +* +* INPUTS +* Buffer - the URL string. +* URL - a pointer to structure, which holds the pointers to the +* the strings of the components. +* +* RESULT +* If the creation of a buffer for the components fails, all components +* of URL will have NULL pointers. If no protocol is found in Buffer, +* URL->Path will contain the complete contents of Buffer (it is +* assumed that Buffer conatins a relative URL). In this case +* URL->Extra whill always be NULL. +* URL->Servername may be NULL, if there was no Servername separator +* ("//") found - this may be the case for URLs pointing to "this" +* Server ("localhost"). +* +****************************************************************************** +* +*/ + +void SplitURL( char *Buffer, struct URLComps *URL ) +{ + char *NewBuffer; + size_t BuffSize; + char *Ptr1; + char *Ptr2; + char *Tmp; + int HaveProto = FALSE; + int CheckSep = FALSE; + int HaveServer = FALSE; + int HavePath = FALSE; + int HaveExtra = FALSE; + + BuffSize = strlen( Buffer ) + 4; + NewBuffer = (char*)mymalloc( BuffSize ); + if( NewBuffer != NULL ) + { + bzero( NewBuffer, BuffSize ); + Tmp = NewBuffer; + for( Ptr1 = Buffer, Ptr2 = NewBuffer; Ptr1 <= Buffer + BuffSize; Ptr1++, Ptr2++ ) + { + if( HaveProto == FALSE ) + { + if( *Ptr1 == ':' ) + { + HaveProto = TRUE; + URL->Protocol = Tmp; + Tmp = Ptr2 + 1; + } + else + { + *Ptr2 = *Ptr1; + } + } + else if( CheckSep == FALSE ) + { + CheckSep = TRUE; + if( *Ptr1 == '/' && *(Ptr1+1) == '/' ) + { + Ptr1++; + } + else + { + HaveServer = TRUE; + URL->Servername = NULL; + Ptr1--; + } + } + else if( HaveServer == FALSE ) + { + if( *Ptr1 == '/' ) + { + HaveServer = TRUE; + URL->Servername = Tmp; + Tmp = Ptr2 + 1; + Ptr1--; + } + else + { + *Ptr2 = *Ptr1; + } + } + else if( HavePath == FALSE ) + { + if( *Ptr1 == '#' || *Ptr1 == '?' ) + { + HavePath = TRUE; + URL->Path = Tmp; + Tmp = Ptr2 + 1; + Ptr1--; + } + else + { + *Ptr2 = *Ptr1; + } + } + else + { + if( HaveExtra == FALSE ) + { + HaveExtra = TRUE; + URL->Extra = Tmp; + } + *Ptr2 = *Ptr1; + } + } + if( HaveProto == FALSE ) + { + URL->Path = Tmp; + } + } + else + { + URL->Protocol = NULL; + URL->Servername = NULL; + URL->Path = NULL; + URL->Extra = NULL; + } + + return; +} + + +/****** http-readmsg/GetContentLength **************************************** +* +* NAME +* GetContentLength - search for the "Content-Length" header in a HTTP +* message and return the value if found. +* +* SYNOPSIS +* size_t GetContentLength( struct MsgHeader *Ptr ); +* +* FUNCTION +* This function searches in a linked list of "MsgHeader" elements +* for one with a "Content_Length" token, and returns the integer +* value of it. +* +* INPUTS +* Ptr - A pointer to a "MsgHeader" node. +* +* RESULT +* 0 if no "Content-Length" header is found, otherwise the value of +* this message header converted into an integer (more precisely: the +* return value of "strtol()"). +* +* BUGS +* The return value of "strtol()" should maybe checked for proper +* results. +* +* SEE ALSO +* GetFieldContent(). +* +****************************************************************************** +* +*/ + +size_t GetContentLength( struct MsgHeader *Ptr ) +{ + while( Ptr != NULL ) + { + if( Ptr->Field == HKEY_Content_Length ) + return( strtol( Ptr->Content, NULL, 10 ) ); + Ptr = Ptr->Next; + } + return( 0 ); +} + + +/****** http-readmsg/GetFieldContent ***************************************** +* +* NAME +* GetFieldContent - Return the content of a specific message header. +* +* SYNOPSIS +* char *GetFieldContent( struct MsgHeader *Ptr, int Field, +* struct MsgHeader **Next ); +* +* FUNCTION +* This function searches in a linked list of "MsgHeader" elements +* for one with the token in the argument "Field", and returns the +* value of it. +* +* INPUTS +* Ptr - A pointer to a "MsgHeader" node. +* Field - The token of the field to search for. +* Next - A pointer to the next "MsgHeader" in the list. +* +* RESULT +* NULL if the header is not found, otherwise a pointer to the content +* string. +* +* SEE ALSO +* GetContentLength(). +* +****************************************************************************** +* +*/ + +char *GetFieldContent( struct MsgHeader *Ptr, int Field, struct MsgHeader **Next ) +{ + while( Ptr != NULL ) + { + if( Ptr->Field == Field ) + { + if( Next != NULL ) + *Next = Ptr->Next; + return( Ptr->Content ); + } + Ptr = Ptr->Next; + } + return( NULL ); +} + + +/****** http-readmsg/GetRealm ************************************************ +* +* NAME +* GetRealm - Search for specific Authentication realms. +* +* SYNOPSIS +* int GetRealm( char *String ); +* +* FUNCTION +* This function searches in the "WWW-Authenticate" header for specific +* authentication realm strings. If one of them is found, the +* appropriate code for this realm is returned. +* +* INPUTS +* String - A pointer to the contents of the "WWW-Authenticate" header +* field. +* +* RESULT +* A realm type code. +* +* BUGS +* "String" is not checked against a NULL pointer. +* +****************************************************************************** +* +*/ + +int GetRealm( char *String ) +{ + struct KeywordKey RealmKeyTable[] = + { + { "httpserver-IPClass-A", REALM_IPCLASS_A }, + { "httpserver-IPClass-B", REALM_IPCLASS_B }, + { "httpserver-IPClass-C", REALM_IPCLASS_C }, + { "httpserver-IPAddress", REALM_IPADDRESS }, + { "httpserver-Hostname", REALM_HOSTNAME }, + { "httpserver-Domain", REALM_DOMAIN }, + }; +#define NUM_REALMS 6 + + char *Buff; + char *Ptr; + char *Tmp; + int i; + + Ptr = String; + if( ( strncasecmp( "realm=", Ptr, 6 ) == 0 ) ) + { + Ptr = Ptr + 6; + if( ( Buff = (char*)mymalloc( strlen( Ptr ) + 1 ) ) == NULL ) + return( REALM_ERROR ); + strcpy( Buff, Ptr ); + + /* Kill Quotes... */ + if( *Ptr == '"' ) + { + Ptr++; + Tmp = strrchr( Ptr, '"' ); + if( Tmp != NULL ) + *Tmp = '\0'; + } + for( i = 0; i < NUM_REALMS; i++ ) + { + if( strcmp( RealmKeyTable[i].Keyword, Ptr ) == 0 ) + { + free( Buff ); + return( RealmKeyTable[i].Key ); + } + } + return( REALM_UNKNOWN ); + } + else + return( REALM_ERROR ); +} diff --git a/httpserver/http-support.cc b/httpserver/http-support.cc new file mode 100644 index 0000000..c4a03b4 --- /dev/null +++ b/httpserver/http-support.cc @@ -0,0 +1,503 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ +#include "mymalloc/mymalloc.h" +/*------------------------------------------------------------------------*/ +/* http-support.c - Support functions for HTTP-Message data. */ +/*------------------------------------------------------------------------*/ +/* Comments: */ +/* - Status: */ +/* - To check: All Buffers free()ed? Also in error cases? */ +/*------------------------------------------------------------------------*/ +/* + * RCS: + * $RCSfile: http-support.c,v $ $Revision: 1.5 $ $State: Exp $ + * $Locker: $ + */ + + +#include "defs.h" +#include "protos.h" +#include "server.h" +#include "http-defs.h" +#include "http.h" + + + +/****** http-support/InitClientBase ****************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +rc_t InitClientBase( struct ClientBase *Client ) +{ + return( ERROR ); +} + + +/****** http-support/InitReqInfo ********************************************* +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +rc_t InitReqInfo( struct ReqInfo *Request ) +{ + Request->HeadBuff = NULL; + Request->HeadSize = 0; + Request->BodyBuff = NULL; + Request->BodySize = 0; + Request->Line.Method = 0; + Request->Line.URL.Protocol = NULL; + Request->Line.URL.Servername = NULL; + Request->Line.URL.Path = NULL; + Request->Line.URL.Extra = NULL; + Request->Line.Version.Major = 0; + Request->Line.Version.Minor = 0; + Request->First = NULL; + Request->Last = NULL; + Request->Body = NULL; + return( OK ); +} + + +/****** http-support/InitRespInfo ******************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +rc_t InitRespInfo( struct RespInfo *Response ) +{ + Response->HeadBuff = NULL; + Response->HeadSize = 0; + Response->BodyBuff = NULL; + Response->BodySize = 0; + Response->Line.Version.Major = 0; + Response->Line.Version.Minor = 0; + Response->Line.Status = 0; + Response->Line.Reason = NULL; + Response->First = NULL; + Response->Last = NULL; + Response->Body = NULL; + return( OK ); +} + + +/****** http-support/InitHTTPMsg ********************************************* +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +void InitHTTPMsg( struct HTTPMsg *Msg ) +{ + Msg->Head = NULL; + Msg->Body = NULL; + Msg->BodySize = 0; +} + + +/****** http-support/NewMsgHeader ******************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +struct MsgHeader *NewMsgHeader( int Key, char *String ) +{ + struct MsgHeader *NewHeader; + + NewHeader = (struct MsgHeader*)mymalloc( sizeof( struct MsgHeader ) ); + if( NewHeader == NULL ) + return( NULL ); + NewHeader->Next = NULL; + NewHeader->Field = Key; + NewHeader->Content = String; + return( NewHeader ); +} + + +/****** http-support/AppendMsgHeader ***************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +struct MsgHeader *AppendMsgHeader( struct MsgHeader *Last, int Key, char *String ) +{ + struct MsgHeader *NewHeader; + + NewHeader = NewMsgHeader( Key, String ); + if( NewHeader == NULL ) + { + ErrorMsg( E_SYS, ERROR, "ERROR: malloc error for MsgHeader structure." ); + return( NULL ); + } + if( Last != NULL ) + { + Last->Next = NewHeader; + } + return( NewHeader ); +} + + +/****** http-support/DeleteMsgHeader ***************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +void DeleteMsgHeader( struct MsgHeader *First ) +{ + struct MsgHeader *Tmp; + + if( First != NULL ) + { + Tmp = First->Next; + free( First ); + First = Tmp; + } + return; +} + + +/****** http-support/PrintReqInfo ******************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +void PrintReqInfo( struct ReqInfo *Request ) +{ + struct MsgHeader *Ptr; + + printf( ">> %s %s HTTP/%d.%d\n", + HTTP_GetMethodName( Request->Line.Method ), + Request->Line.URL.Protocol ? Request->Line.URL.Protocol : "", + Request->Line.URL.Servername ? Request->Line.URL.Servername : "", + Request->Line.URL.Path ? Request->Line.URL.Path : "", + Request->Line.URL.Extra ? Request->Line.URL.Extra : "", + Request->Line.Version.Major, Request->Line.Version.Minor ); + + Ptr = Request->First; + while( Ptr != NULL ) + { + printf( ">> %s: %s\n", HTTP_GetFieldName( Ptr->Field ), Ptr->Content ); + Ptr = Ptr->Next; + } + return; +} + + +/****** http-support/PrintRespInfo ******************************************* +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +void PrintRespInfo( struct RespInfo *Response ) +{ + struct MsgHeader *Ptr; + + printf( ">> HTTP/%d.%d %d %s\n", + Response->Line.Version.Major, Response->Line.Version.Minor , + Response->Line.Status, + Response->Line.Reason ); + + Ptr = Response->First; + while( Ptr != NULL ) + { + printf( ">> %s: %s\n", HTTP_GetFieldName( Ptr->Field ), Ptr->Content ); + Ptr = Ptr->Next; + } + return; +} + + +/****** http-support/CheckSockError ****************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +rc_t CheckSockError( int SockFD, int Level, int OptName ) +{ + int error; + size_t len = sizeof( error ); + +#ifdef DECALPHA + if( getsockopt( SockFD, Level, OptName, (char *)&error, (int*)&len ) < 0 ) +#else + if( getsockopt( SockFD, Level, OptName, (char *)&error, (socklen_t*)&len ) < 0 ) +#endif + return( FAIL ); + if( error ) + { + errno = error; + return( ERROR ); + } + return( OK ); +} + + diff --git a/httpserver/http-writemsg.cc b/httpserver/http-writemsg.cc new file mode 100644 index 0000000..20d430f --- /dev/null +++ b/httpserver/http-writemsg.cc @@ -0,0 +1,314 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ +#include "mymalloc/mymalloc.h" +/*------------------------------------------------------------------------*/ +/* http-writemsg.c - generic HTTP-Message functions: */ +/* Creating and sending of HTTP-Messages. */ +/*------------------------------------------------------------------------*/ +/* Comments: */ +/* - Status: */ +/* - To check: All Buffers free()ed? Also in error cases? */ +/*------------------------------------------------------------------------*/ +/* + * RCS: + * $RCSfile: http-writemsg.c,v $ $Revision: 1.3 $ $State: Exp $ + * $Locker: $ + */ + + +#include "defs.h" +#include "protos.h" +#include "types.h" +#include "server.h" +#include "http-defs.h" +#include "http.h" + +extern struct HTTPError HTTPErrorTable[]; + + +/****** http-writemsg/AddField *********************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +rc_t AddField( struct MsgHeader *Ptr, int Field, char *Content ) +{ + struct MsgHeader *NewHeader; + + if( Ptr != NULL ) + while( Ptr->Next != NULL ) + Ptr = Ptr->Next; + + NewHeader = AppendMsgHeader( Ptr, Field, Content ); + if( NewHeader != NULL ) + return( OK ); + else + return( WARN ); +} + + +/****** http-writemsg/CreateStatusLine *************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +rc_t CreateStatusLine( char *Buffer, size_t *BuffSize, int Code, int Protocol ) +{ + int Entry; + + Entry = GetHTTPErrorTableEntry( Code ); + switch( Protocol ) + { + case HTTP_1_0: + { + if( Entry != 0 ) + SNPrintf( Buffer, BuffSize, "HTTP/1.0 %d %s\r\n", Code, HTTPErrorTable[Entry].Reason ); + else + SNPrintf( Buffer, BuffSize, "HTTP/1.0 %d Statuscode %d\r\n", Code, Code ); + return( OK ); + } + break; + case HTTP_1_1: + { + if( Entry != 0 ) + SNPrintf( Buffer, BuffSize, "HTTP/1.1 %d %s\r\n", Code, HTTPErrorTable[Entry].Reason ); + else + SNPrintf( Buffer, BuffSize, "HTTP/1.1 %d Statuscode %d\r\n", Code, Code ); + return( OK ); + } + break; + default: + { + return( ERROR ); + } + } +} + + +/****** http-writemsg/CreateHTTPMsg ****************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +struct HTTPMsg *CreateHTTPMsg( char *Header, char *Body, size_t BodySize ) +{ + struct HTTPMsg *Msg; + + /* printf( "### CreateHTTPMsg(+)\n" ); */ + Msg = (struct HTTPMsg*)mymalloc( sizeof( struct HTTPMsg ) ); + if( Msg != NULL ) + { + /* printf( "### Header[%d]:\n%s", strlen( Header ), Header ); */ + /* printf( "### Body[%d]:\n", BodySize ); */ + + Msg->Body = Body; + Msg->BodySize = BodySize; + + if( ( Msg->Head = (char*)mymalloc( strlen( Header ) + 1 ) ) != NULL ) + strcpy( Msg->Head, Header ); + else + { + free( Msg ); + return( NULL ); + } + /* printf( "### Done!\n" ); */ + } + /* printf( "### CreateHTTPMsg(-)\n" ); */ + return( Msg ); +} + + +/****** http-writemsg/SendHTTPMsg ******************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +rc_t SendHTTPMsg( int SockFD, struct HTTPMsg *Msg ) +{ + size_t Check; + + if( ( Msg != NULL ) && ( Msg->Head != NULL ) ) + { + Check = WriteN( SockFD, Msg->Head, strlen(Msg->Head) ); + if( Check < 0 ) + ErrorMsg( E_SYS, ERROR, "ERROR: WriteN(): write() failed." ); + else if( Check != strlen(Msg->Head) ) + LogMsg( LG_SERVER, WARN, + "WARN: SendHTTPMsg(): %d of %d Bytes of Header written!", + Check, strlen(Msg->Head) ); + + if( ( Msg->Body != NULL ) && ( Msg->BodySize > 0 ) ) + { + Check = WriteN( SockFD, Msg->Body, Msg->BodySize ); + if( Check < 0 ) + ErrorMsg( E_SYS, ERROR, "ERROR: WriteN(): write() failed." ); + else if( Check != Msg->BodySize ) + LogMsg( LG_SERVER, WARN, + "WARN: SendHTTPMsg(): %d of %d Bytes of Body written!", + Check, Msg->BodySize ); + } + return( OK ); + } + else + { + return( ERROR ); + } +} + + +/****** http-writemsg/FreeHTTPMsg ******************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +rc_t FreeHTTPMsg( struct HTTPMsg *Ptr ) +{ + free( Ptr->Head ); + free( Ptr ); + return( OK ); +} diff --git a/httpserver/http.cc b/httpserver/http.cc new file mode 100644 index 0000000..898be85 --- /dev/null +++ b/httpserver/http.cc @@ -0,0 +1,181 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ +/*------------------------------------------------------------------------*/ +/* http.c - reworked HTTP communication main loop. */ +/*------------------------------------------------------------------------*/ + + +#include "defs.h" +#include "protos.h" +#include "server.h" +#include "http-defs.h" +#include "http.h" + +/* + * Release ClientBase + */ +void releaseClientBase(struct ClientBase *cb) +{ + // free Host + free(cb->Host.Name); + + // free Socket + // struct sockaddr_in Socket; + + // free TimeOut + // struct timeval TimeOut; + + // free Request + free(cb->Request.HeadBuff); + free(cb->Request.BodyBuff); + struct MsgHeader *dummy = NULL; + while(cb->Request.First != NULL) + { + dummy = cb->Request.First->Next; + //free(cb->Request.First->Content); + free(cb->Request.First); + cb->Request.First = dummy; + } + free(cb->Request.Body); + free(cb->Request.Line.Vanilla); + + // free Response + free(cb->Response.Head); + free(cb->Response.Body); + +} + + +/****** http/HandleRequest *************************************************** +* +* NAME +* HandleRequest -- main function for communication processing. +* +* SYNOPSIS +* void HandleRequest( struct ClientBase *Client, +* SubServerPtr SubServerList ); +* +* FUNCTION +* This is the main function which handles the HTTP communication. +* It calls successivley the functions for handling the request from +* the client, opening the sockets to the sub-servers and sending +* the rewritten requests to them, handling the responses and finally +* sending a response to the client. +* +* INPUTS +* Client - Data structure which holds various informations about +* the client. +* SubServerList - The head to a list of all sub-server data structures. +* +* NOTES +* "Client" and "SubServerList" must be valid - they will not be +* checked. Since "Client" have to be valid in the previous call to +* "Accept()" this shouldn't be much of a problem... +* +* This is "rewritten from scratch" version of "HandleRequest()" +* since the old version seemed not suitable enough for expansion +* to handle more error conditions in the communication, and for +* integration of more features. +* +* BUGS +* Various left. Especially the authorization mechanism doesn't work +* (yet). Also, some aspects of the HTTP aren't handled gracefully +* (HTTP/0.9 messages, handling of other methods than GET, HEAD and +* POST, etc.). +* +****************************************************************************** +* +*/ +void HandleRequest( struct ClientBase *Client ) +{ + struct ToDoArgs ToDo; + int RespState; + + ToDo.What = DO_NOTHING; + ToDo.Which.Code = REALLY_NOTHING; + Client->Comm.ConnStatus = CONN_OPEN; + + while( Client->Comm.ConnStatus == CONN_OPEN ) + { + //LogMsg( LG_SERVER, DEBUG, "DEBUG: HandleRequest(): Reading Request."); + GetRequest( Client ); + //LogMsg( LG_SERVER, DEBUG, "DEBUG: HandleRequest(): Parsing Request."); + ParseReqHeader( &Client->Request ); + + //LogMsg( LG_SERVER, DEBUG, "DEBUG: HandleRequest(): Interpreting Request."); + InterpreteRequest( Client, &ToDo ); + switch( ToDo.What ) + { + case DO_SEND_RESPONSE: + //LogMsg( LG_SERVER, DEBUG, "DEBUG: HandleRequest(): doing InterpreteRequest."); + InterpretePOSTRequest( Client ); + + //LogMsg( LG_SERVER, DEBUG, "DEBUG: HandleRequest(): doing CreateRasResponse."); + CreateRasResponse( &Client->Comm, Client ); + /* + LogMsg( LG_SERVER, DEBUG, "DEBUG: SendResponse() for Request: \"%s\".", + Client->Request.Line.Vanilla ); + */ + + SendHTTPMsg( Client->SockFD, &Client->Response ); + WriteAccessLog( Client ); + LogMsg( LG_SERVER, DEBUG, "DEBUG: HandleRequest(): SEND_RESPONSE done."); + break; + + case DO_SEND_ERROR: + LogMsg( LG_SERVER, DEBUG, "DEBUG: HandleRequest(): doing SEND_ERROR." ); + Client->RespStatus = ToDo.Which.Code; + LogMsg( LG_SERVER, DEBUG, "DEBUG: CreateHTTPError( %d, -, %p )...", + Client->RespStatus, &Client->Response ); + CreateHTTPError( Client->RespStatus, &Client->Comm, &Client->Response ); + LogMsg( LG_SERVER, DEBUG, "DEBUG: CreateHTTPError() done." ); + LogMsg( LG_SERVER, DEBUG, "DEBUG: SendResponse()..." ); + SendResponse( Client ); + LogMsg( LG_SERVER, DEBUG, "DEBUG: SendResponse() done." ); + LogMsg( LG_SERVER, DEBUG, "DEBUG: Write*Log()..." ); + WriteAccessLog( Client ); + LogMsg( LG_SERVER, DEBUG, "DEBUG: WriteLog() done." ); + LogMsg( LG_SERVER, DEBUG, "DEBUG: HandleRequest(): SEND_ERROR done."); + break; + + case DO_SHUTDOWN: + LogMsg( LG_SERVER, DEBUG, "DEBUG: HandleRequest(): doing SHUTDOWN."); + close( Client->SockFD ); + LogMsg( LG_SERVER, DEBUG, "DEBUG: HandleRequest(): SHUTDOWN done."); + break; + + default: + LogMsg( LG_SERVER, DEBUG, "DEBUG: HandleRequest(): doing UNDEFINED ACTION."); + ErrorMsg( E_PRIV, ERROR, "ERROR: HandleRequest(): Undefined action!" ); + Client->RespStatus = STATUS_Internal_Server_Error; + CreateHTTPError( Client->RespStatus, &Client->Comm, &Client->Response ); + SendResponse( Client ); + WriteAccessLog( Client ); + LogMsg( LG_SERVER, DEBUG, "DEBUG: HandleRequest(): UNDEFINED ACTION done."); + break; + } + releaseClientBase(Client); + + } +} + diff --git a/httpserver/http.h b/httpserver/http.h new file mode 100644 index 0000000..f22ee49 --- /dev/null +++ b/httpserver/http.h @@ -0,0 +1,133 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ +/*------------------------------------------------------------------------*/ +/* http.h - typedefs and structures for HTTP. */ +/*------------------------------------------------------------------------*/ +/* + * RCS: + * $RCSfile: http.h,v $ $Revision: 1.3 $ $State: Exp $ + * $Locker: $ + */ + +#ifndef _HTTP_H +#define _HTTP_H + + +struct KeywordKey +{ + char *Keyword; + int Key; +}; + +struct HTTPError +{ + int Code; + char *Reason; + char *Message_en; + char *Message_de; +}; + +/* HTTP Message structure + */ + +struct HTTPMsg +{ + char *Head; + char *Body; + int BodySize; +}; + + +struct URLComps +{ + char *Protocol; + char *Servername; + char *Path; + char *Extra; +}; + + +struct Version +{ + int Major; + int Minor; +}; + + +struct RequestLine +{ + char *Vanilla; + int Method; + struct URLComps URL; + struct Version Version; +}; + +struct StatusLine +{ + char *Vanilla; + struct Version Version; + int Status; + char *Reason; +}; + +struct MsgHeader +{ + struct MsgHeader *Next; + int Field; + char *Content; +}; + + +/* Data Structure for HTTP Request + */ + +struct ReqInfo +{ + int State; + char *HeadBuff; + size_t HeadSize; + char *BodyBuff; + size_t BodySize; + struct RequestLine Line; + struct MsgHeader *First; + struct MsgHeader *Last; + char *Body; +}; + +/* Data Structure for HTTP Response + */ + +struct RespInfo +{ + int State; + char *HeadBuff; + size_t HeadSize; + char *BodyBuff; + size_t BodySize; + struct StatusLine Line; + struct MsgHeader *First; + struct MsgHeader *Last; + char *Body; +}; + +#endif /* _HTTP_H not defined */ diff --git a/httpserver/httpserver.conf b/httpserver/httpserver.conf new file mode 100644 index 0000000..a56c060 --- /dev/null +++ b/httpserver/httpserver.conf @@ -0,0 +1,14 @@ +# +# httpserver Configuration file +# +ServerName: sunwibas15.forwiss.tu-muenchen.de +Port: 8080 + +ServerAdmin: widmann@forwiss.tu-muenchen.de + +ServerRoot: /home/gast/beinhofe/rman/ +AccessLog: /home/gast/beinhofe/rman/httpserver/httpaccess.log +ServerLog: /home/gast/beinhofe/rman/httpserver/httpserver.log +PidFile: /home/gast/beinhofe/rman/httpserver/httpserver.pid +MaxURLLength: 120 + diff --git a/httpserver/httpserver.h b/httpserver/httpserver.h new file mode 100644 index 0000000..9c95c5b --- /dev/null +++ b/httpserver/httpserver.h @@ -0,0 +1,38 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/*** + * INCLUDE: httpserver.h + * + * MODULE: httpserver + * CLASS: none, C header + * + * COMMENTS: + * +*/ + +#ifndef _HTTPSERVERC_ +#define _HTTPSERVERC_ + +extern int init_httpserver( int argc, char *argv[] ); + +#endif diff --git a/httpserver/init.cc b/httpserver/init.cc new file mode 100644 index 0000000..b58ce35 --- /dev/null +++ b/httpserver/init.cc @@ -0,0 +1,319 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ + +#include "mymalloc/mymalloc.h" +/*------------------------------------------------------------------------*/ +/* init.c - Functions to setup the server. */ +/*------------------------------------------------------------------------*/ + +#include <iostream> + +#include "defs.h" +#include "protos.h" +#include "server.h" +#include "http-defs.h" +#include "http.h" + +extern struct ServerBase Server; + + +/****** init/Initialize ****************************************************** +* +* NAME +* Initialize -- central function for initializing the servers data +* structures and environment. +* +* SYNOPSIS +* int Initialize( int argc, char *argv[], struct ServerBase *Server ); +* +* FUNCTION +* +* RESULT +* +* NOTES +* +* BUGS +* +* SEE ALSO +* +****************************************************************************** +* +*/ + +rc_t Initialize( int argc, char *argv[], struct ServerBase *Server ) +{ + + /* Make sure that the Server structure is empty. -------------------- */ + bzero( (char *)Server, sizeof( *Server ) ); + + /* Create Head of ChildList */ + Server->ChildList = (struct ChildBase*)mymalloc( sizeof( struct ChildBase ) ); + if( Server->ChildList == NULL ) + ErrorMsg( E_SYS, FAIL, "FAIL: Initialize(): malloc() failed!" ); + else + { + //LogMsg( LG_SERVER, DEBUG, "DEBUG: Creating Head of ChildList" ); + Server->ChildList->next = Server->ChildList; + Server->ChildList->prev = Server->ChildList; + Server->ChildList->PId = 0; + } + + /* Read the environment variables */ + char* dummy = CONFDIR; + if( dummy == NULL) + ErrorMsg( E_SYS, FAIL, "FAIL: Environment variable RMANHOME is not set!" ); + Server->Directory = (char*)mymalloc(strlen(dummy) + 2); + strcpy(Server->Directory, dummy); + if( Server->Directory[strlen(Server->Directory)] != '/' ) + strcat( Server->Directory, "/" ); + + /* Read Arguments and Server configuration file - and setup --------- */ + /* the corresponding data structures. --------------------------- */ + + ReadArgs( Server, argc, argv ); + //ReadConfig( Server ); + + ConfigureServer( Server ); + // CheckConfig( Server ); // Check for reasonable configuration + + /* Set timeout for communication with subservers and client --------- */ + Server->Client.TimeOut.tv_sec = DIALOG_TIMEOUT; + Server->Client.TimeOut.tv_usec = 0; + + /* Get the process into "daemon"-mode. ------------------------------ */ + InitDaemon( Server->Log.Mode ); + Server->PId = getpid(); + + //SavePId( Server->PidFile ); + /* Initialize the Server Socket. ------------------------------------ */ + InitSocket( &Server->SockFD, &Server->Socket, Server->Port ); + + /* Setup and Open the logfiles. ------------------------------------- */ + OpenLog( &Server->Log, Server->Log.Access.Filename, + Server->Log.Server.Filename, + Server->Log.Comm.Filename ); + + + LogMsg( LG_SERVER, INFO, "INFO: ========= %s started. ===============", DAEMONNAME ); + + // We don't do that so that the one process running can be exited with simple ^C + /* Initialize the signal handlers. ---------------------------------- */ + // InitSigHandler(); + + /* Everything is just great... -------------------------------------- */ + return( OK ); +} + + +/****** init/InitDaemon ****************************************************** +* +* NAME +* InitDaemon -- going into "daemon"-mode: process forks to run in the +* background and closes all open file-descriptors. +* +* SYNOPSIS +* int InitDaemon( int Mode ); +* +* FUNCTION +* +* RESULT +* +* NOTES +* +* BUGS +* +* SEE ALSO +* +****************************************************************************** +* +*/ + +rc_t InitDaemon( int Mode ) +{ + int i; + pid_t pid; + int openmax; + + // RasDaMan does not go into background! + chdir( Server.Directory ); /* Arbeitsverzeichnis wechseln */ + umask( 0 ); /* Dateischutzbitmaske loeschen */ + return( OK ); +} + + +/****** init/InitSocket ****************************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* ### Minimum Socket Setup. Stay tuned. +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +rc_t InitSocket( int *SockFD, struct sockaddr_in *Socket, int Port ) +{ + int val = 0; + size_t len; + + Socket->sin_family = AF_INET; + Socket->sin_addr.s_addr = htonl( INADDR_ANY ); + Socket->sin_port = htons( Port ); + if( ( *SockFD = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 ) + ErrorMsg( E_SYS, FAIL, "FAIL: Open socket for server." ); + +#ifdef SO_REUSEADDR + val = 1; + len = sizeof( val ); + if( setsockopt( *SockFD, SOL_SOCKET, SO_REUSEADDR, (char*)&val, len ) < 0 ) + ErrorMsg( E_SYS, WARN, "WARN: InitSocket(): can't set sockopt REUSEADDR." ); +#else + LogMsg( LG_SERVER, INFO, "INFO: InitSocket(): sockopt REUSEADDR not available." ); +#endif + + if( bind( *SockFD, (struct sockaddr *)Socket, sizeof( *Socket ) ) < 0 ) + ErrorMsg( E_SYS, FAIL, "FAIL: Bind socket for server." ); + return( OK ); +} + + +/****** init/InitClientSocket ************************************************ +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +rc_t InitClientSocket( int *SockFD, + struct sockaddr_in *Socket, + char *Hostname, + int Port ) +{ + struct hostent *Host; + + bzero( (char *)Socket, sizeof( *Socket ) ); + + Socket->sin_family = AF_INET; + Host = gethostbyname( Hostname ); + if( Host == NULL ) + { + return( ERROR ); + } + memcpy( (char *)&Socket->sin_addr, (char *)Host->h_addr, Host->h_length ); + + Socket->sin_port = htons( Port ); + if( ( *SockFD = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 ) + return( ERROR ); + return( OK ); +} + + +/****** init/SavePId ********************************************************* +* +* NAME +* SavePId -- store process id in file. +* +* SYNOPSIS +* void SavePId( char *PIdFilename ); +* +* FUNCTION +* THis function saves the parent process id in a file, so this file +* can be used by for example shell scripts to identify the correct +* process to signal it. +* +* INPUTS +* PIdFilename -- Full pathname of the file which stortes the pid. +* +* RESULT +* Does not return anything. +* +* NOTES +* This function is called by Initialize(), right after the logs are +* opened. So the content of the pidfile is also only valid afterwards. +* To ensure, that there is no stale pidfile, it is deleted on normal +* termination. +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + + +void SavePId( char *PIdFilename ) +{ + FILE *PIdFile; + + if( ( PIdFile = fopen( PIdFilename, "w" ) ) != NULL ) + { + fprintf( PIdFile, "%d", getpid() ); + fclose( PIdFile ); + } +} diff --git a/httpserver/logging.cc b/httpserver/logging.cc new file mode 100644 index 0000000..e486c66 --- /dev/null +++ b/httpserver/logging.cc @@ -0,0 +1,297 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ +/*------------------------------------------------------------------------*/ +/* logging.c - logging and error message handling. */ +/*------------------------------------------------------------------------*/ + +#include "defs.h" +#include "protos.h" +#include "server.h" + + +extern struct Logging *LogBase; + + +rc_t OpenLog( struct Logging *Log, char *AccessLog, char *ServerLog, char *CommLog ) +{ + /* removed this, we don't need this logs + if( (Log->Mode & LF_STDERR) == LF_STDERR ) // Log to STDOUT/STDERR ? + { + Log->Access.Filename = NULL; + Log->Access.FD = STDOUT_FILENO; + Log->Access.State = FILE_OPEN; + Log->Server.Filename = NULL; + Log->Server.FD = STDERR_FILENO; + Log->Server.State = FILE_OPEN; + Log->Comm.Filename = NULL; + Log->Comm.FD = STDOUT_FILENO; + Log->Comm.State = FILE_OPEN; + } + else + { + Log->Access.Filename = AccessLog; + if( AccessLog != NULL ) + { + Log->Access.FD = open( Log->Access.Filename, + O_WRONLY | O_CREAT | O_APPEND, FILE_MODE ); + if( Log->Server.FD < 0 ) + ErrorMsg( E_SYS, FAIL, "FAIL: Open AccessLog failed." ); + Log->Access.State = FILE_OPEN; + } + else + { + Log->Access.State = FILE_CLOSED; + } + + Log->Server.Filename = ServerLog; + if( ServerLog != NULL ) + { + Log->Server.FD = open( Log->Server.Filename, + O_WRONLY | O_CREAT | O_APPEND, FILE_MODE ); + if( Log->Server.FD < 0 ) + ErrorMsg( E_SYS, FAIL, "FAIL: Open ServerLog failed." ); + Log->Server.State = FILE_OPEN; + } + else + { + Log->Server.State = FILE_CLOSED; + } + } + */ + + Log->Server.State = FILE_CLOSED; + Log->Access.State = FILE_CLOSED; + Log->Comm.State = FILE_CLOSED; + return( OK ); +} + + +void CloseLog( struct Logging *Log ) +{ + //close( Log->Server.FD ); + Log->Server.State = FILE_CLOSED; + //close( Log->Access.FD ); + Log->Access.State = FILE_CLOSED; +} + + + +void ErrorMsg( int errnoFlag, int ErrLevel, const char *ErrMsg, ... ) +{ + int errno_save; + char ErrMsgBuffer[ MAXLINELEN ]; + size_t StringSize, BuffSize; + va_list ArgPtr; + + errno_save = errno; + + va_start( ArgPtr, ErrMsg ); + if( VSNPrintf( ErrMsgBuffer, MAXLINELEN, ErrMsg, ArgPtr ) < 0) + ErrorMsg( E_PRIV, WARN, "WARN: Log message exceeded buffer size!" ); + va_end( ArgPtr ); + + StringSize = strlen( ErrMsgBuffer ); + BuffSize = MAXLINELEN - StringSize; + + if( errnoFlag ) + { + if( SNPrintf( ErrMsgBuffer + StringSize, &BuffSize, + " Reason: %s", StrError( errno_save ) ) < 0) + { + ErrorMsg( E_PRIV, WARN, "WARN: Log message exceeded buffer size!" ); + } + } + LogMsg( LG_SERVER, ErrLevel, ErrMsgBuffer ); + + switch( ErrLevel ) + { + case DUMP: + { + abort(); /* core dump erzeugen und beenden */ + exit( DUMP ); /* sollte nicht soweit kommen... */ + } + break; + case FAIL: + { + exit( FAIL ); /* Ausstieg mit Status "FAIL". */ + } + break; + default: + return; + } +} + + +void LogMsg( int SubSys, int Level, const char *Msg, ... ) +{ + return; + /* + char MsgBuffer[ MAXLINELEN ]; + char *BufferPtr; + size_t BuffSize = MAXLINELEN; + size_t TimeBuffSize = DATE_BUFFSIZE; + va_list ArgPtr; + + if( (Level == DEBUG) && ((LogBase->Mode & LF_VERB) == 0) ) // DEBUG Msgs wanted? + return; + + bzero( MsgBuffer, MAXLINELEN ); + BufferPtr = &MsgBuffer[0]; + + // ### HACK! access.log datestamp is already in msg string! + if( SubSys != LG_ACCESS ) + { + LogDate( BufferPtr, TimeBuffSize ); + strcat( BufferPtr, " " ); + } + if( SubSys == LG_SERVER ) + { + BufferPtr = &MsgBuffer[0] + strlen( MsgBuffer ); + sprintf( BufferPtr, "<%d> ", getpid() ); + } + + BufferPtr = &MsgBuffer[0] + strlen( MsgBuffer ); + BuffSize = MAXLINELEN - strlen( MsgBuffer ) - 2; + + va_start( ArgPtr, Msg ); + if( VSNPrintf( BufferPtr, BuffSize , Msg, ArgPtr ) < 0) + ErrorMsg( E_PRIV, WARN, "WARN: Log message exceeded buffer size!" ); + va_end( ArgPtr ); + + // Log-Meldungen sollten immer ein <EOL> am Ende haben. + // Also kontrollieren und nötigenfalls ergänzen. + // Da MsgBuffer mit maximal MAXLINELEN - 2 Zeichen gefüllt sein + // sollte, kann gefahrlos <EOL> angehängt werden. + + if( MsgBuffer[ strlen( MsgBuffer ) - 1 ] != '\n' ) + strcat( MsgBuffer, "\n" ); + + // Moeglichkeiten: + // stdio offen, Server.Log nicht initialisiert + // stdio offen, Server.Log initialisiert (Log auf stdio) + // stdio geschlossen, Server.Log initialisiert (Log in File) + + switch( SubSys ) + { + case LG_SERVER: + if( LogBase->Server.State == FILE_OPEN ) + write( LogBase->Server.FD, MsgBuffer, strlen( MsgBuffer ) ); + else + write( STDERR_FILENO, MsgBuffer, strlen( MsgBuffer ) ); + break; + case LG_ACCESS: + if( LogBase->Access.State == FILE_OPEN ) + write( LogBase->Access.FD, MsgBuffer, strlen( MsgBuffer ) ); + else + write( STDOUT_FILENO, Msg, strlen( MsgBuffer ) ); + break; + case LG_COMM: + if( LogBase->Comm.State == FILE_OPEN ) + write( LogBase->Comm.FD, MsgBuffer, strlen( MsgBuffer ) ); + else + write( STDOUT_FILENO, Msg, strlen( MsgBuffer ) ); + break; + default: + ErrorMsg( E_PRIV, WARN, "WARN: Unknown Logging sub-system!" ); + } + */ + return; +} + +rc_t LogDate( char *Buffer, int BuffSize ) +{ + struct tm *ltime; + int TZoffset; + char sign; + + size_t strsize; + size_t RestSize; + + ltime = Get_GMToffset( &TZoffset ); + + /* LOG Date Format: "[dd/mmm/yyyy:HH:MM:SS {-|+}XXXX]" */ + /* XXXX = Timezone offset format: HHMM */ + sign = ( TZoffset < 0 ? '-' : '+' ); + if( TZoffset < 0 ) + TZoffset = -TZoffset; + + strsize = strftime( Buffer, BuffSize, "[%d/%b/%Y:%H:%M:%S ", ltime ); + if( strsize >= BuffSize-6 || strsize == 0 ) + return( ERROR ); + else + { + RestSize = BuffSize-strsize; + SNPrintf( Buffer+strsize, &RestSize, "%c%.2d%.2d]", + sign, TZoffset/60, TZoffset%60 ); + return( OK ); + } +} + + +/****** logging/Get_GMToffset ************************************************ +* +* NAME +* Get_GMToffset -- Get numerical offset of localtime to gmtime. +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* Source code taken from Apache 1.3.1 distribution. +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +struct tm *Get_GMToffset( int *tz ) +{ + time_t tt = time(NULL); + struct tm gmt; + struct tm *t; + int days, hours, minutes; + + /* Assume we are never more than 24 hours away. */ + gmt = *gmtime(&tt); /* remember gmtime/localtime return ptr to static */ + t = localtime(&tt); /* buffer... so be careful */ + days = t->tm_yday - gmt.tm_yday; + hours = ((days < -1 ? 24 : 1 < days ? -24 : days * 24) + + t->tm_hour - gmt.tm_hour); + minutes = hours * 60 + t->tm_min - gmt.tm_min; + *tz = minutes; + return( t ); +} diff --git a/httpserver/main.cc b/httpserver/main.cc new file mode 100644 index 0000000..5082fe9 --- /dev/null +++ b/httpserver/main.cc @@ -0,0 +1,191 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ + +#include "raslib/error.hh" +#include "raslib/rminit.hh" + +#include "httpserver.h" + +#include "defs.h" +#include "protos.h" +#include "types.h" +#include "server.h" + +#ifdef PURIFY +#include <purify.h> +#endif + +/*--- Global Data Section: ------------------------------------------------*/ +/* All we need to remember or may want to know is in this structure */ + +struct ServerBase Server; + +struct Logging *LogBase = &Server.Log; + +/****** main/main ************************************************************ +* +* NAME +* main -- the main function of this program. +* +* SYNOPSIS +* void main( int argc, char *argv[] ); +* +* FUNCTION +* "main" calls "Initialize()" for setting up the program, activates +* the main socket with the "listen()" system call, and then waits +* forever for a client to connect. If a client connection request is +* received, a child process will be spawned and the child will handle +* the request while the parent process will wait again for new +* connection requests. +* +* INPUTS +* argc - The number of arguments given on the command line. +* argv[] - An array of strings, containing the arguments given on the +* command line. +* +* NOTES +* The program can only be stopped by sending it the corresponding +* signals ("SIGQUIT", "SIGTERM"). +* +* BUGS +* None known. Not here. +* +* SEE ALSO +* Initialize(), Accept(), AddChild(), InitChild(), HandleRequest(). +* +****************************************************************************** +* +*/ + +extern int init_httpserver( int argc, char *argv[] ) +{ + pid_t ChildPId; /* -> Server.ChildInfo */ + + RMInit::logOut << "Initialising parameters for HTTP server... " << endl; + RMInit::logOut.flush(); + Initialize( argc, argv, &Server ); + + RMInit::logOut << "Initialising server socket for HTTP server... " << endl; + RMInit::logOut.flush(); + listen( Server.SockFD, 5 ); + RMInit::logOut << "Waiting for client calls... " << endl; + RMInit::logOut.flush(); + +#ifdef PURIFY + purify_printf( "Server Startup Finnished." ); + purify_new_leaks(); +#endif + +/* // this is a quick hack for testing */ +/* return 0; */ + for(;;) + { + Accept( Server.SockFD, &Server.Client ); + + strcpy( Server.Client.Host.IPAddrString, inet_ntoa( Server.Client.Socket.sin_addr ) ); + + if( Server.Client.Host.IPAddrString == NULL ) + strcpy( Server.Client.Host.IPAddrString, "0.0.0.0" ); + + Server.Client.Host.IPAddress = inet_addr( Server.Client.Host.IPAddrString ); + Server.Client.Comm.ConnStatus = CONN_UNDEFINED; + InitHTTPMsg( &Server.Client.Response ); + InitReqInfo( &Server.Client.Request ); + + LogMsg( LG_SERVER, INFO, "INFO: ====== Connection from %s accepted...", + Server.Client.Host.IPAddrString ); + HandleRequest( &Server.Client ); + LogMsg( LG_SERVER, INFO, "INFO: ====== EOT. Disconnecting." ); + + close( Server.Client.SockFD ); + + } + // otherwise Exit(OK) should have been called + return -1; +} + + + +/****** main/Exit ************************************************************ +* +* NAME +* Exit -- Cleanup when exiting. +* +* SYNOPSIS +* void Exit( int RC ); +* +* FUNCTION +* This function closes all open socket descriptors and the logging +* subsystem. If the calling process is the parent process, it also +* deletes the file where the process Id is stored. +* After that, it calls the "exit()" function with the return code +* "RC". +* +* INPUTS +* RC - return or exit code, indicating the error condition of the +* program. +* +* NOTES +* This function is called by the signal handlers for "SIGQUIT", +* "SIGTERM", "SIGINT" and "SIGHUP" and the main() function. +* In future versions, "SIGHUP" should be changed to reread the +* configuration file instead of calling Exit(). +* +* BUGS +* The global variable "SubServer[]" should not be used. +* +* SEE ALSO +* Exit(). +* +****************************************************************************** +* +*/ + +int Exit( int RC ) +{ + int i; + + // for( i = 0; i < NUM_SUBSERVER; i++ ) + // close( SubServer[i].SockFD ); + close( Server.SockFD ); + + if( getpid() == Server.PId ) /* Is this the parent process? */ + { + unlink( Server.PidFile ); + LogMsg( LG_SERVER, DEBUG, "DEBUG: Main process exiting (RC=%d)...", RC ); + LogMsg( LG_SERVER, INFO, + "INFO: ========= %s terminated. ============", DAEMONNAME ); + } + else + { + LogMsg( LG_SERVER, DEBUG, "DEBUG: Exiting (RC=%d)...", RC ); + } + + CloseLog( &Server.Log ); + + free(Server.Directory); + + if(RC != OK) throw r_Error( r_Error::r_Error_General ); + return RC; +} + diff --git a/httpserver/protos.h b/httpserver/protos.h new file mode 100644 index 0000000..60a0ac9 --- /dev/null +++ b/httpserver/protos.h @@ -0,0 +1,178 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ +/*------------------------------------------------------------------------*/ +/* protos.h - Prototypes of all functions */ +/*------------------------------------------------------------------------*/ +/* + * RCS: + * $RCSfile: protos.h,v $ $Revision: 1.10 $ $State: Exp $ + * $Locker: $ + */ + +#ifndef _PROTOS_H +#define _PROTOS_H + +#include "server.h" + +/*------------------------------------------------------------------------*/ +/* Protoypes from main.c */ +/*------------------------------------------------------------------------*/ + +int Exit( int ); + +/* init.c */ +rc_t Initialize( int, char **, struct ServerBase * ); +rc_t InitDaemon( int ); +rc_t InitSocket( int *, struct sockaddr_in *, int ); +rc_t InitClientSocket( int *, struct sockaddr_in *, char *, int ); +void SavePId( char * ); + +/* childs.c */ +rc_t InitChild( struct ClientBase * ); +pid_t NewChild( struct ChildBase *List, struct FDsets *PDSets, struct ClientBase *Client ); +void CleanupChild( struct ChildBase *List, struct FDsets *PDSets, pid_t PId ); +void AddChild( struct ChildBase *List, struct ChildBase *Child ); +void RemChild( struct ChildBase *List, struct ChildBase *Child ); +struct ChildBase *GetChild( struct ChildBase *List, pid_t PId ); + +/*------------------------------------------------------------------------*/ +/* Protoypes from config.c */ +/*------------------------------------------------------------------------*/ + +rc_t ReadArgs( struct ServerBase *, int, char ** ); +rc_t ReadConfig( struct ServerBase * ); +rc_t CheckAndSet( struct ServerBase *, char *, int ); +rc_t SetServDir( struct ServerBase *, char * ); +rc_t SetFilename( struct ServerBase *, int, char * ); +rc_t SetString( struct ServerBase *, int, char * ); +rc_t ConfigureServer( struct ServerBase * ); +int GetConfigKey( char * ); + +/*------------------------------------------------------------------------*/ +/* Protoypes from signals.c */ +/*------------------------------------------------------------------------*/ + +typedef void(*sighandler)(int); + +sighandler Signal( int, sighandler ); +void SigHandler( int ); +rc_t InitSigHandler( void ); + +/*------------------------------------------------------------------------*/ +/* Protoypes from logging.c */ +/*------------------------------------------------------------------------*/ + +rc_t OpenLog( struct Logging *, char *, char *, char * ); +void CloseLog( struct Logging *Log ); +void LogMsg( int, int, const char *, ... ); +void ErrorMsg( int, int, const char *, ... ); +rc_t LogDate( char *, int ); +struct tm *Get_GMToffset( int * ); + +/*------------------------------------------------------------------------*/ +/* Protoypes for communication handling. */ +/*------------------------------------------------------------------------*/ + +/* http.c */ +void HandleRequest( struct ClientBase *Client ); + +/* http-doit.c */ +rc_t Accept( int, struct ClientBase * ); +void GetRequest( struct ClientBase *Client ); +void InterpreteRequest( struct ClientBase *Client, struct ToDoArgs *ToDo ); +int SendRequest( int SockFD, struct HTTPMsg *Request, struct FDsets *RW_Sets ); +int ReadResponseHead( int SockFD, struct RespInfo *Response, struct FDsets *RW_Sets ); +int ReadResponseBody( int SockFD, struct RespInfo *Response, struct FDsets *RW_Sets ); +void SendResponse( struct ClientBase *Client ); +void CreateRasResponse( struct HTTPMode *Mode, struct ClientBase *Client ); +void DoMessageBody( struct ClientBase *Client ); +void InterpretePOSTRequest ( struct ClientBase *Client ); +void WriteAccessLog( struct ClientBase *Client ); + + +/* http-readmsg.c */ +int ReadHeader( int SockFD, char **Buffer, size_t *BuffSize ); +char *ReadBody( int SockFD, size_t BuffSize ); +rc_t ParseReqHeader( struct ReqInfo *Request ); +rc_t ParseRespHeader( struct RespInfo *Response ); +char *ParseReqLine( char *Buffer, struct ReqInfo *Request ); +char *ParseRespLine( char *Buffer, struct RespInfo *Response ); +char *ParseMsgLine( char *Buffer, int *Key, char **Param ); +void SplitURL( char *Buffer, struct URLComps *URL ); +size_t GetContentLength( struct MsgHeader *Ptr ); +char *GetFieldContent( struct MsgHeader *Ptr, int Field, struct MsgHeader **Next ); +int GetRealm( char *String ); + +/* http-writemsg.g */ +rc_t AddField( struct MsgHeader *Ptr, int Field, char *Content ); +rc_t CreateStatusLine( char *Buffer, size_t *BuffSize, int Code, int Protocol ); +struct HTTPMsg *CreateHTTPMsg( char *, char *, size_t ); // ??? +rc_t SendHTTPMsg( int SockFD, struct HTTPMsg *Msg ); +rc_t FreeHTTPMsg( struct HTTPMsg *Ptr ); + +/* http-error.c */ +int GetHTTPErrorTableEntry( int Code ); +rc_t CreateHTTPError( int Code, struct HTTPMode *Mode, struct HTTPMsg *Msg ); + +/* http-methods.c */ +int HTTP_GetMKey( char * ); +char *HTTP_GetMethodName( int ); + +/* http-fields.c */ +int HTTP_GetHKey( char * ); +char *HTTP_GetFieldName( int ); + +/* http-date.c */ +rc_t HTTP_Date( char *, size_t ); + +/* http-support.c */ +rc_t InitClientBase( struct ClientBase * ); +rc_t InitReqInfo( struct ReqInfo * ); +rc_t InitRespInfo( struct RespInfo * ); +void InitHTTPMsg( struct HTTPMsg *Msg ); +struct MsgHeader *NewMsgHeader( int, char * ); +struct MsgHeader *AppendMsgHeader( struct MsgHeader *, int, char * ); +void DeleteMsgHeader( struct MsgHeader * ); +void PrintReqInfo( struct ReqInfo * ); +void PrintRespInfo( struct RespInfo * ); +rc_t CheckSockError( int, int, int ); +char *CharToBits( char c ); + + + +/*------------------------------------------------------------------------*/ +/* Protoypes from support.c */ +/*------------------------------------------------------------------------*/ + +int Get_OpenMax( void ); +char *PathAlloc( size_t * ); +int ReadN( register int, register char *, register int ); +int WriteN( register int, register char *, register int ); +int ReadLine( register int, register char *, register int ); +rc_t ParseString( char *, char *, ... ); +int SNPrintf( char *, size_t *, const char *, ... ); +int VSNPrintf( char *, size_t, const char *, va_list ); +char *StrError( int ); +char *StrToLower( char * ); + +#endif /* _PROTOS_H not defined */ diff --git a/httpserver/server.h b/httpserver/server.h new file mode 100644 index 0000000..e3b3f54 --- /dev/null +++ b/httpserver/server.h @@ -0,0 +1,190 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ +/*------------------------------------------------------------------------*/ +/* server.h - server structures. */ +/*------------------------------------------------------------------------*/ + + +#ifndef _SERVER_H +#define _SERVER_H + +#include <sys/types.h> +#include <unistd.h> +#include <limits.h> +#include <errno.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> + +#include <signal.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#ifndef AIX + #include <sys/fcntl.h> +#endif + +#include <fcntl.h> +#include <string.h> +#include <strings.h> +#include <sys/time.h> +#include <time.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include "types.h" +#include "http.h" + +struct HTTPRequest +{ + char *Database; + int Command; + char *QueryString; + int ClientType; + char *ClientID; + int Endianess; + int NumberOfQueryParams; + char *BinData; + int BinDataSize; + char *Capability; +}; + +union WhichArg +{ + int Code; +}; + +struct ToDoArgs +{ + int What; + union WhichArg Which; +}; + +struct FDsets +{ + int MaxFD; + fd_set Read; + fd_set Write; +}; + +struct LogFile +{ + char *Filename; + int FD; + int State; +}; + + +struct Logging +{ + int Mode; + struct LogFile Server; + struct LogFile Access; + struct LogFile Comm; +}; + +struct ChildBase +{ + struct ChildBase *next; + struct ChildBase *prev; + pid_t PId; + int PD[2]; + int PipeStatus; +}; + + +struct Host +{ + char *Name; + char IPAddrString[ 16 ]; +#ifdef NO_in_addr_t + unsigned long IPAddress; +#else + in_addr_t IPAddress; +#endif +}; + + +struct HTTPMode +{ + int Protocol; + int ConnStatus; +}; + + +struct ClientBase +{ + /* Client Host Infos */ + struct Host Host; + int ClientType; + /* Socket */ + int SockFD; + struct sockaddr_in Socket; + int SockSize; + /* select() timeout */ + struct timeval TimeOut; + + struct HTTPMode Comm; + /* Request */ + struct ReqInfo Request; + /* Response */ + struct HTTPMsg Response; + int RespStatus; + /* Pipe to Parent process */ + int Pipe; + char PipeBuffer[PIPE_BUFFSIZE]; +}; + + +struct ServerBase +{ + pid_t PId; + /* -- Config.Information */ + struct Host Host; + int Port; + char *AdminMailAddress; + char *Directory; + char *ConfigFile; + char *PidFile; + //char *CacheFile; + size_t MaxURLLength; + /* -- Status Information */ + int Status; + struct ChildBase *ChildList; + struct FDsets PipeSets; + /* -- Global Data */ + struct Logging Log; + struct CacheNode *Cache; + /* -- Server Socket */ + int SockFD; + struct sockaddr_in Socket; + /* -- Client Information */ + struct ClientBase Client; +}; + + +#endif /* _SERVER_H not defined */ diff --git a/httpserver/signals.cc b/httpserver/signals.cc new file mode 100644 index 0000000..4bda368 --- /dev/null +++ b/httpserver/signals.cc @@ -0,0 +1,287 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ +/*------------------------------------------------------------------------*/ +/* signals.c - signal handling stuff. */ +/*------------------------------------------------------------------------*/ +/* + * RCS: + * $RCSfile: signals.c,v $ $Revision: 1.8 $ $State: Exp $ + * $Locker: $ +*/ + + +#include "defs.h" +#include "protos.h" +#include "server.h" + +extern struct ServerBase Server; + + +/****** signals/Signal ******************************************************* +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* Own Signal() function, based on sigaction(). +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +sighandler Signal( int signo, sighandler x) +{ + struct sigaction SigAction; + struct sigaction OldAction; + + SigAction.sa_handler = (void(*)(int))SigHandler; + sigemptyset( & SigAction.sa_mask ); + SigAction.sa_flags = 0; + + if( signo == SIGALRM ) + { +#ifdef SA_INTERRUPT + SigAction.sa_flags |= SA_INTERRUPT; /* SunOS */ +#endif + } + else + { +#ifdef SA_RESTART + SigAction.sa_flags |= SA_RESTART; /* SVR4, 4.3+ BSD */ +#endif + } + + if( sigaction( signo, &SigAction, &OldAction ) < 0 ) + return( SIG_ERR ); + return( OldAction.sa_handler ); +} + + +/****** signals/SigHandler *************************************************** +* +* NAME +* SigHandler -- Little Signal Handler. +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +void SigHandler( int Signal ) +{ + //not used(see line 169) char Buffer[PIPE_BUFFSIZE]; + int errno_bak; + int ChildStatus; + pid_t ChildPId; + struct ChildBase *Child = NULL; + + errno_bak = errno; + switch( Signal ) + { + case SIGHUP: + LogMsg( LG_SERVER, INFO, "INFO: Received 'SIGHUP'." ); + errno = errno_bak; + Exit( OK ); + break; + + case SIGINT: + LogMsg( LG_SERVER, INFO, "INFO: Received 'SIGINT'." ); + errno = errno_bak; + Exit( OK ); + break; + + case SIGQUIT: + LogMsg( LG_SERVER, INFO, "INFO: Received 'SIGQUIT'." ); + errno = errno_bak; + Exit( OK ); + break; + + case SIGUSR1: + LogMsg( LG_SERVER, INFO, "INFO: Received 'SIGUSR1'." ); + // SaveCache( Server.Cache, Server.CacheFile ); + break; + + case SIGUSR2: + LogMsg( LG_SERVER, INFO, "INFO: Received 'SIGUSR2'." ); + break; + + case SIGPIPE: + LogMsg( LG_SERVER, INFO, "INFO: Received 'SIGPIPE'." ); + break; + + case SIGTERM: + LogMsg( LG_SERVER, INFO, "INFO: Received 'SIGTERM'." ); + errno = errno_bak; + Exit( OK ); + break; + + case SIGCHLD: + LogMsg( LG_SERVER, INFO, "INFO: Received 'SIGCHLD'." ); + ChildPId = waitpid( -1, &ChildStatus, WNOHANG ); + while( ChildPId > 0 ) + { + if( WIFEXITED( ChildStatus ) ) + { + /*-- Has Child send a Message? --*/ + if( WEXITSTATUS( ChildStatus ) == NOTE ) + { + Child = GetChild( Server.ChildList, ChildPId ); + if( Child != NULL ) + { + /* + bzero( Buffer, PIPE_BUFFSIZE ); + read( Child->PD[0], Buffer, PIPE_BUFFSIZE ); + HandlePipeMsg( Buffer, Server.SubServerList, Server.Cache ); + */ + } + } + LogMsg( LG_SERVER, DEBUG, + "DEBUG: Child <%d> exited normally with status %d.", + ChildPId, WEXITSTATUS( ChildStatus ) ); + } + else if( WIFSIGNALED( ChildStatus ) ) + { + LogMsg( LG_SERVER, DEBUG, + "DEBUG: Child <%d> exited abnormally, signal number = %d.", + ChildPId, WTERMSIG( ChildStatus ) ); + } + else if( WIFSTOPPED( ChildStatus ) ) + { + LogMsg( LG_SERVER, DEBUG, + "DEBUG: Child <%d> is stopped, signal number = %d.", + ChildPId, WSTOPSIG( ChildStatus ) ); + } + else + { + LogMsg( LG_SERVER, WARN, + "WARN: Something strange with Child <%d> (Status: %d).", + ChildPId, ChildStatus ); + } + + CleanupChild( Server.ChildList, &Server.PipeSets, ChildPId ); + ChildPId = waitpid( -1, &ChildStatus, WNOHANG ); + + } + if( ChildPId == -1 && errno != ECHILD ) + ErrorMsg( E_SYS, WARN, "WARN: waitpid() returned an Error.", errno ); + LogMsg( LG_SERVER, DEBUG, "DEBUG: Exiting Signal Handler ..." ); + break; + default: + ErrorMsg( E_PRIV, ERROR, "ERROR: Signal %d couldn't be handled!", Signal ); + break; + } + errno = errno_bak; + return; +} + + +/****** signals/InitSigHandler *********************************************** +* +* NAME +* InitSigHandler -- Minimum Signal Handler setup. +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ + +rc_t InitSigHandler( void ) +{ + if( Signal( SIGHUP, SigHandler ) == SIG_ERR ) + ErrorMsg( E_SYS, ERROR, "ERROR: Installing Signal Handler (HUP)." ); + if( Signal( SIGINT, SigHandler ) == SIG_ERR ) + ErrorMsg( E_SYS, ERROR, "ERROR: Installing Signal Handler (INT)." ); + if( Signal( SIGQUIT, SigHandler ) == SIG_ERR ) + ErrorMsg( E_SYS, ERROR, "ERROR: Installing Signal Handler (QUIT)." ); + if( Signal( SIGTERM, SigHandler ) == SIG_ERR ) + ErrorMsg( E_SYS, ERROR, "ERROR: Installing Signal Handler (TERM)." ); + if( Signal( SIGCHLD, SigHandler ) == SIG_ERR ) + ErrorMsg( E_SYS, ERROR, "ERROR: Installing Signal Handler (CHLD)." ); + if( Signal( SIGUSR1, SigHandler ) == SIG_ERR ) + ErrorMsg( E_SYS, ERROR, "ERROR: Installing Signal Handler (USR1)." ); + if( Signal( SIGUSR2, SigHandler ) == SIG_ERR ) + ErrorMsg( E_SYS, ERROR, "ERROR: Installing Signal Handler (USR2)." ); + + if( Signal( SIGPIPE, SigHandler ) == SIG_ERR ) + ErrorMsg( E_SYS, ERROR, "ERROR: Installing Signal Handler (PIPE)." ); + return( OK ); +} + diff --git a/httpserver/support.cc b/httpserver/support.cc new file mode 100644 index 0000000..6a011fd --- /dev/null +++ b/httpserver/support.cc @@ -0,0 +1,640 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ +#include "mymalloc/mymalloc.h" +/*------------------------------------------------------------------------*/ +/* support.c - support functions for the gateway httpd. */ +/*------------------------------------------------------------------------*/ +/* + * RCS: + * $RCSfile: support.c,v $ $Revision: 1.6 $ $State: Exp $ + * $Locker: $ + */ + +#include <ctype.h> + +#include "defs.h" +#include "types.h" +#include "protos.h" +#include "server.h" + + +/****** support/Get_OpenMax ************************************************** +* +* NAME +* Get_OpenMax -- get the maximum of file descriptors per process. +* +* SYNOPSIS +* int Get_OpenMax( void ); +* +* FUNCTION +* Get the systems maximum number of file descriptors per process. Uses +* OPEN_MAX -- if defined -- else asks the system per sysconf() call. +* If this still doesn't succeed, returns some kind of "reasonable +* default". +* +* INPUTS +* openmax - A global variable that holds the value of OPEN_MAX, +* if defined, else '0'. +* +* RESULT +* Returns the number of file descriptors, may be zero or a positive +* int value. +* openmax will be set to an appropriate value if it was set to '0' +* when Get_OpenMax() was called. +* +* NOTES +* Uses ErrMsg() to report problems with sysconf(). Hmmm... +* +* BUGS +* Does also return the guessed value if sysconf() reported an error. +* This seems to be okay for my needs... +* +* SEE ALSO +* Richard W. Stevens: "Advanced Programming in the UNIX Environment", +* 1992 Addison-Wesley Publishing Company, Inc. +* +****************************************************************************** +* +*/ + +#ifdef OPEN_MAX +static size_t openmax = OPEN_MAX; /* Wenn vorhanden, dann ist das alles */ +#else /* was gebraucht wird. */ +static size_t openmax = 0; +#endif + +#ifdef NOFILE +#define OPEN_MAX_GUESS NOFILE +#else +#define OPEN_MAX_GUESS 256 /* Falls OPEN_MAX undefiniert (dynamisch) */ +#endif /* ist, ist dies u.U. nicht adaequat! */ + +int Get_OpenMax( void ) +{ + if( openmax == 0 ) + { + errno = 0; + if( ( openmax = sysconf( _SC_OPEN_MAX ) ) < 0 ) + { + if( errno == 0 ) + { + openmax = OPEN_MAX_GUESS; + } + else + { + /* sysconf() returned an error... strange, but... */ + /* Does it justify to break the program? */ + /* Naah, I don't think so... So we just print a note */ + /* and give back our "reasonable default"... */ + ErrorMsg( E_SYS, WARN, "WARN: sysconf error for _SC_OPEN_MAX." ); + openmax = OPEN_MAX_GUESS; + } + } + } + + return( openmax ); +} + + +/****** support/PathAlloc **************************************************** +* +* NAME +* PathAlloc -- allocate memory as buffer for pathnames. +* +* SYNOPSIS +* char *PathAlloc( size_t *size ); +* +* FUNCTION +* Get the systems maximum of characters in absolute pathnames. Uses +* PATH_MAX -- if defined -- else asks the system per pathconf() call. +* If this still doesn't succeed, uses some kind of "reasonable +* default". +* Then it calls malloc() to allocate the buffer. +* +* INPUTS +* size - A pointer to a variable, which will set to the size of +* the buffer. If it is NULL, you won't get any size +* information. +* pathmax - A global variable that holds the value of PATH_MAX, +* if defined, else '0' +* +* RESULT +* Returns a "char" pointer to the allocated buffer. +* NULL, if the allocation failed. Then it will also print a log +* message. +* pathmax will be set to an appropriate value if it was set to '0' +* when PathAlloc() was called. +* +* NOTES +* Uses ErrMsg() to report problems with pathconf() and malloc(). +* +* BUGS +* Does also use the guessed value if pathconf() reported an error. +* This seems to be okay for my needs... +* +* SEE ALSO +* Richard W. Stevens: "Advanced Programming in the UNIX Environment", +* 1992 Addison-Wesley Publishing Company, Inc. +* +****************************************************************************** +* +*/ + +#ifdef PATH_MAX +static size_t pathmax = PATH_MAX; /* Wenn vorhanden, dann ist das alles */ +#else /* was gebraucht wird. */ +static size_t pathmax = 0; +#endif + +#ifdef MAXPATHLEN +#define PATH_MAX_GUESS MAXPATHLEN +#else +#define PATH_MAX_GUESS 1024 /* Falls PATH_MAX undefiniert (dynamisch) */ +#endif /* ist, ist dies u.U. nicht adäquat! */ + +char *PathAlloc( size_t *size ) +{ + char *Ptr; + + if( pathmax == 0 ) + { + errno = 0; + if( ( pathmax = pathconf( "/", _PC_PATH_MAX ) ) < 0 ) + { + if( errno == 0 ) + { + pathmax = PATH_MAX_GUESS; + } + else + { + /* pathconf() returned an error... strange, but... */ + /* See also Get_OpenMax(). */ + /* We give back our "reasonable default"... */ + ErrorMsg( E_SYS, WARN, "WARN: pathconf error for _PC_PATH_MAX." ); + pathmax = PATH_MAX_GUESS; + } + } + } + else + { + // it appears that every call makes the path 1 byte longer. Anyway, the config file + // is not read too often and I am not willing to debug it now + pathmax++; /* Don't forget the "/" character... */ + } + + if( ( Ptr = (char*)mymalloc( pathmax + 1 ) ) == NULL ) + { + if( size != NULL ) + *size = 0; + ErrorMsg( E_SYS, ERROR, "ERROR: malloc error for pathname buffer." ); + } + else + { + if( size != NULL ) + *size = pathmax + 1; + } + return( Ptr ); +} + + +/****** support/ReadN ******************************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ +/************************************************************************** + * Read "n" bytes from a descriptor. + * Use in place of read() when fd is a stream socket. + * Checks for error 'EAGAIN', in case of nonblocking IO. + */ + +int ReadN( register int fd, register char *ptr, register int nbytes ) +{ + int nleft; + int nread; + + nleft = nbytes; + while( nleft > 0 ) + { + nread = read( fd, ptr, nleft ); + if( nread < 0 ) + if( errno != EAGAIN ) + return( nread ); /* error, return < 0 */ + else + continue; + else if( nread == 0 ) + break; /* EOF */ + nleft -= nread; + ptr += nread; + } + return( nbytes - nleft ); /* return >= 0 */ +} + + +/****** support/WriteN ******************************************************* +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ +/************************************************************************** + * Write "n" bytes to a descriptor. + * Use in place of write() when fd is a stream socket. + * Checks for error 'EAGAIN', in case of nonblocking IO. + */ + +int WriteN( register int fd, register char *ptr, register int nbytes ) +{ + long nleft; + long nwritten = 0; + + nleft = nbytes; + while( nleft > 0 ) + { + nwritten = write( fd, ptr, nleft ); + if( nwritten < 0 ) + { + if( errno != EAGAIN ) + return( nwritten ); /* error */ + else + continue; + } + nleft -= nwritten; + ptr += nwritten; + } + return( nbytes - nleft ); +} + + +/****** support/ReadLine ***************************************************** +* +* NAME +* +* +* SYNOPSIS +* +* +* FUNCTION +* +* +* INPUTS +* +* +* RESULT +* +* +* NOTES +* +* +* BUGS +* +* +* SEE ALSO +* +* +****************************************************************************** +* +*/ +/************************************************************************** + * Read a line from a descriptor. + * Checks for error 'EAGAIN', in case of nonblocking IO. + */ + +int ReadLine( register int fd, register char *ptr, register int maxlen ) +{ + int n; + int rc; + char c; + + for( n = 1; n < maxlen; n++ ) + { + if( ( rc = read( fd, &c, 1 ) ) == 1 ) + { + *ptr++ = c; + if( c == '\n' ) + break; + } + else if( rc == 0 ) + if( n == 1 ) + return( 0 ); /* EOF, no data read */ + else + break; /* EOF, some data was read */ + else + if( errno != EAGAIN ) + return( -1 ); /* error */ + } + *ptr = 0; + return( n ); +} + + +/****** support/ParseString ************************************************** +* +* NAME +* ParseString -- Break string into words. +* +* SYNOPSIS +* int ParseString( char *String, char *Token, ... ); +* +* FUNCTION +* +* RESULT +* +* NOTES +* +* BUGS +* +* SEE ALSO +* +****************************************************************************** +* +*/ + +rc_t ParseString( char *String, char *Token, ... ) +{ + /* ###TODO: Das letzte Argument sollte den kompletten Reststring erhalten */ + /* Buffer loeschen vor Austritt */ + va_list ArgPtr; + char *Keyword; + char *Value; + char *NextToken; + char *NNextToken; + char *Delim = " \t\n\r"; /* Begrenzungszeichen */ + + if( ( String[0] == '#' ) || /* Kommentar, */ + ( String[0] == '\n' ) || /* oder Leerzeile? */ + ( String[0] == '\r' ) ) + return( ERROR ); /* => Zurück. */ + + Keyword = strtok( String, Delim ); /* 1. Token suchen */ + if( Keyword != NULL ) /* Etwas gefunden? */ + { + if( Keyword[0] == '#' ) /* Auch Kommentarzeile: */ + return( ERROR ); /* => Zurück. */ + strcpy( Token, Keyword ); /* Keyword in Buffer kopieren */ + + /* Var.Arg. Liste abarbeiten bis Arg == NULL. */ + /* Letztes Arg. muss NULL sein, da va_arg() nicht selbst in der Lage */ + /* ist das letzte Argument zu erkennen. */ + + va_start( ArgPtr, Token ); /* VarArg initialisieren */ + NextToken = va_arg( ArgPtr, char * ); /* 1. zus. Argument holen. */ + if( NextToken != NULL) + { + NNextToken = va_arg( ArgPtr, char * ); /* 2. zus. Arg. holen */ + while( NNextToken != NULL ) /* Argument vorhanden? */ + { + Value = strtok( NULL, Delim ); /* Weiteres Token suchen */ + if( Value != NULL ) /* Wort gefunden? */ + strcpy( NextToken, Value ); /* Token in Buffer kopieren */ + else /* Ansonsten: */ + strcpy( NextToken, "\0" ); /* Leerstring uebergeben */ + NextToken = NNextToken; + NNextToken = va_arg( ArgPtr, char * ); /* Zeiger auf nächstes Arg. */ + } + Value = strtok( NULL, "\n\r" ); /* Zeiger auf Reststring holen */ + if( Value != NULL ) /* Wenn vorhanden, */ + strcpy( NextToken, Value ); /* in letztes Arg. übergeben */ + else /* wenn nicht, */ + strcpy( NextToken, "\0" ); /* Leerstring übergeben */ + } + va_end( ArgPtr ); /* VarArg aufräumen */ + return( OK ); /* OK: Es wurde etwas gefunden */ + } + return( ERROR ); /* ERROR: String war leer. */ +} + + +/****** support/SNPrintf ***************************************************** +* +* NAME +* SNPrintf -- print into buffer with size-check. +* +* SYNOPSIS +* int SNPrintf( char *String, size_t *Size, const char *Format, ... ); +* +* FUNCTION +* +* RESULT +* +* NOTES +* +* BUGS +* +* SEE ALSO +* +****************************************************************************** +* +*/ + +int SNPrintf( char *String, size_t *Size, const char *Format, ... ) +{ + va_list ArgPtr; + int n = 0; + + if( *Size > 0 ) + { + va_start( ArgPtr, Format ); + n = VSNPrintf( String, *Size, Format, ArgPtr ); + va_end( ArgPtr ); + + if( n >= 0 ) + *Size = *Size - n; + else + *Size = 0; + } + return( n ); +} + + +/****** support/VSNPrintf **************************************************** +* +* NAME +* VSNPrintf -- print variable argument list into buffer with size-check. +* +* SYNOPSIS +* int VSNPrintf( char *String, size_t Size, +* const char *Format, va_list ArgPtr ); +* +* FUNCTION +* +* RESULT +* +* NOTES +* +* BUGS +* +* SEE ALSO +* +****************************************************************************** +* +*/ + +int VSNPrintf( char *String, size_t Size, const char *Format, va_list ArgPtr ) +{ + int n = 0; + char *Buffer = NULL; + +#ifdef NO_vsnprintf + if( Size == 0 ) + n = 0; + else if( Size > 0 ) + { + if( ( Buffer = (char*)mymalloc( BUFFSIZE ) ) != NULL ) + { + n = vsprintf( Buffer, Format, ArgPtr ); + if( n < Size ) + strncpy( String, Buffer, Size ); + else + { + strncpy( String, Buffer, Size-1 ); + *( String + Size ) = '\0'; + n = -1; + } + free( Buffer ); + } + else + n = -1; + } + else + n = -1; +#else + n = vsnprintf( String, Size, Format, ArgPtr ); +#endif + return( n ); +} + + +/****** support/StrError ***************************************************** +* +* NAME +* StrError - return error string for given error code. +* +* SYNOPSIS +* char *StrError( int ErrNum ); +* +* FUNCTION +* +* RESULT +* +* NOTES +* +* BUGS +* +* SEE ALSO +* +****************************************************************************** +* +*/ + +char *StrError( int ErrNum ) +{ +#ifdef NO_strerror + char Buffer[20]; + + bzero( Buffer, 20 ); + sprintf( Buffer, "Error %d", ErrNum ); + return( Buffer ); +#else + return( strerror( ErrNum ) ); +#endif +} + +/****** support/StrToLower *************************************************** +* +* NAME +* StrToLower - make all characters of a string lower case. +* +* SYNOPSIS +* char *StrToLower( char *String ); +* +* FUNCTION +* +* RESULT +* +* NOTES +* +* BUGS +* +* SEE ALSO +* +****************************************************************************** +* +*/ + +char *StrToLower( char *String ) +{ + while( *String != '\0' ) + { + *String = tolower( *String ); + String++; + } + return( String ); +} diff --git a/httpserver/test/Makefile b/httpserver/test/Makefile new file mode 100644 index 0000000..2fef649 --- /dev/null +++ b/httpserver/test/Makefile @@ -0,0 +1,63 @@ +# -*-Makefile-*- (for Emacs) +# +# 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 <http://www.gnu.org/licenses/>. +# +# Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +# rasdaman GmbH. +# +# For more information please see <http://www.rasdaman.org> +# or contact Peter Baumann via <baumann@rasdaman.com>. # Top Level makefile. This points to the various modules that have to be build +# and/or deployed +# +# MAKEFILE FOR: +# a RasDaMan module +# +# COMMENTS: +# This Makefile was created during the relational port when +# splitting up catalogif into O2 dependent und not O2 dependent. +# +# IMPORTANT: +# As opposed to all other Makefiles catalogmgr does not have +# its own library. catalogif is used instead, otherwise the +# linking of all executables would have had to be changed. +# +################################################################## + +# +# IMPORTANT: +# This is an example Makefile, it has to be adapted for +# every module. This is usually done by editing the +# following: +# * variables OBJS, TESTOBJS and TESTPRG +# * the dependencies at the end +# * every occurence of EXLIB +# * the toplevel Makefile and Makefile.inc +# + +######################### Definitions ############################ + +# standard include with general options +include $(RMANBASE)/Makefile.inc + +MISCCLEAN := core + +########################### Targets ############################## + +# general rules +include $(RMANBASE)/Makefile.rel + +# automatically created dependencies +include Makefile.dep diff --git a/httpserver/test/httpserver.conf b/httpserver/test/httpserver.conf new file mode 100644 index 0000000..63ae3fd --- /dev/null +++ b/httpserver/test/httpserver.conf @@ -0,0 +1,14 @@ +# +# httpserver Configuration file +# +ServerName: sunwibas13.forwiss.tu-muenchen.de +Port: 8080 + +ServerAdmin: zoller@forwiss.tu-muenchen.de + +ServerRoot: /home/sunwibas13/wiss/zoller/RASDAMAN/httpserver/test +AccessLog: /home/sunwibas13/wiss/zoller/RASDAMAN/httpserver/test/access.log +ServerLog: /home/sunwibas13/wiss/zoller/RASDAMAN/httpserver/test/server.log +PidFile: /home/sunwibas13/wiss/zoller/RASDAMAN/httpserver/test/httpserver.pid +MaxURLLength: 120 + diff --git a/httpserver/test/post_test.html b/httpserver/test/post_test.html new file mode 100644 index 0000000..46aab47 --- /dev/null +++ b/httpserver/test/post_test.html @@ -0,0 +1,116 @@ +<HEAD> +<TITLE>Error</TITLE> +</HEAD> + +<BODY> +<H1>IMPORTANT NOTE</H1> + +<P>Unfortunately requests to the RasDaMan HTTP server are cached by + default. Be sure to disable the proxy in your webserver and also + disable the internal cache by telling the browser to verify the + document every time.</P> + + +<CENTER> +<P> +<BR> +<H1>Test for Post-Request: Client is Browser</H1> + +<FORM ACTION="http://sunwibas0.forwiss.tu-muenchen.de:8080" METHOD=POST> +<INPUT TYPE=HIDDEN NAME="Database" VALUE="NorbertBase"> +<INPUT TYPE=HIDDEN NAME="QueryString" VALUE="SELECT img FROM test_image AS img"> +<INPUT TYPE=HIDDEN NAME="ClientType" VALUE="BROWSER"> + +<INPUT TYPE=SUBMIT VALUE="Test: Everything correct!"> +</FORM> +<P> + +<FORM ACTION="http://sunwibas0.forwiss.tu-muenchen.de:8080" METHOD=POST> +<INPUT TYPE=HIDDEN NAME="Database" VALUE="NorbertBase"> +<INPUT TYPE=HIDDEN NAME="QueryString" VALUE="SELECT img FROM test_image AS img"> +<INPUT TYPE=HIDDEN NAME="ClientType" VALUE="xyz"> + +<INPUT TYPE=SUBMIT VALUE="Test: Wrong Client Type"> +</FORM> +<P> + +<FORM ACTION="http://sunwibas0.forwiss.tu-muenchen.de:8080" METHOD=POST> +<INPUT TYPE=HIDDEN NAME="Database" VALUE="NorbertBase"> +<INPUT TYPE=HIDDEN NAME="ClientType" VALUE="BROWSER"> +<INPUT TYPE=HIDDEN NAME="TestParam" VALUE="wfwef"> + +<INPUT TYPE=SUBMIT VALUE="Test: Unknown Post Parameters"> +</FORM> +<P> + +<P> +<A HREF="http://sunwibas0.forwiss.tu-muenchen.de:8080/">Get-request</A> + + +<H1>Testing different return types</H1> + +<FORM ACTION="http://sunwibas0.forwiss.tu-muenchen.de:8080" METHOD=POST> +<INPUT TYPE=HIDDEN NAME="Database" VALUE="NorbertBase"> +<INPUT TYPE=HIDDEN NAME="QueryString" VALUE="SELECT count_cells(img > 9l) FROM test_image AS img"> +<INPUT TYPE=HIDDEN NAME="ClientType" VALUE="RASCLIENT"> + +<INPUT TYPE=SUBMIT VALUE="Returns scalar"> +</FORM> +<P> + +<FORM ACTION="http://sunwibas0.forwiss.tu-muenchen.de:8080" METHOD=POST> +<INPUT TYPE=HIDDEN NAME="Database" VALUE="NorbertBase"> +<INPUT TYPE=HIDDEN NAME="QueryString" VALUE="SELECT sdom(img) FROM test_image AS img"> +<INPUT TYPE=HIDDEN NAME="ClientType" VALUE="RASCLIENT"> + +<INPUT TYPE=SUBMIT VALUE="Returns spatial domain"> +</FORM> +<P> + +<FORM ACTION="http://sunwibas0.forwiss.tu-muenchen.de:8080" METHOD=POST> +<INPUT TYPE=HIDDEN NAME="Database" VALUE="NorbertBase"> +<INPUT TYPE=HIDDEN NAME="QueryString" VALUE="SELECT grmblfx(img) FROM test_image AS img"> +<INPUT TYPE=HIDDEN NAME="ClientType" VALUE="RASCLIENT"> + +<INPUT TYPE=SUBMIT VALUE="Returns error"> +</FORM> +<P> + +<FORM ACTION="http://sunwibas0.forwiss.tu-muenchen.de:8080" METHOD=POST> +<INPUT TYPE=HIDDEN NAME="Database" VALUE="NorbertBase"> +<INPUT TYPE=HIDDEN NAME="QueryString" VALUE="SELECT img FROM rockies AS img"> +<INPUT TYPE=HIDDEN NAME="ClientType" VALUE="RASCLIENT"> + +<INPUT TYPE=SUBMIT VALUE="Retrieve whole rockies"> +</FORM> +<P> + +<P> + + +<H1>Test for Post-Request: Client is RasClient</H1> + +<FORM ACTION="http://sunwibas0.forwiss.tu-muenchen.de:8080" METHOD=POST> +<INPUT TYPE=HIDDEN NAME="Database" VALUE="NorbertBase"> +<INPUT TYPE=HIDDEN NAME="QueryString" VALUE="SELECT img FROM test_image AS img"> +<INPUT TYPE=HIDDEN NAME="ClientType" VALUE="RASCLIENT"> + +<INPUT TYPE=SUBMIT VALUE="Test: Everything correct!"> +</FORM> +<P> + +<FORM ACTION="http://sunwibas0.forwiss.tu-muenchen.de:8080" METHOD=POST> +<INPUT TYPE=HIDDEN NAME="Database" VALUE="NorbertBase"> +<INPUT TYPE=HIDDEN NAME="ClientType" VALUE="RASCLIENT"> +<INPUT TYPE=HIDDEN NAME="TestParam" VALUE="wfwef"> + +<INPUT TYPE=SUBMIT VALUE="Test: Unknown Post Parameters"> +</FORM> +<P> + + + +</CENTER> + +</BODY> + diff --git a/httpserver/types.h b/httpserver/types.h new file mode 100644 index 0000000..dab5bb0 --- /dev/null +++ b/httpserver/types.h @@ -0,0 +1,38 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ +/*------------------------------------------------------------------------*/ +/* types.h - data types. */ +/*------------------------------------------------------------------------*/ +/* + * RCS: + * $RCSfile: types.h,v $ $Revision: 1.1 $ $State: Exp $ + * $Locker: $ + */ + +#ifndef _TYPES_H +#define _TYPES_H + +typedef struct SubServerBase *SubServerPtr; +typedef int rc_t; + +#endif /* _TYPES_H not defined */ |