diff options
Diffstat (limited to 'ACE/apps/JAWS/server')
29 files changed, 5368 insertions, 0 deletions
diff --git a/ACE/apps/JAWS/server/HTTP_Config.cpp b/ACE/apps/JAWS/server/HTTP_Config.cpp new file mode 100644 index 00000000000..11890037d3c --- /dev/null +++ b/ACE/apps/JAWS/server/HTTP_Config.cpp @@ -0,0 +1,140 @@ +// $Id$ + +// HTTP_Config.cpp + +#include "ace/OS_NS_stdlib.h" +#include "HTTP_Config.h" + +ACE_RCSID(server, HTTP_Config, "$Id$") + +// static HTTP_Config_Info config_info; + +HTTP_Config_Info *HTTP_Config::instance_ = 0; + +HTTP_Config_Info * +HTTP_Config::instance (void) +{ + if (HTTP_Config::instance_ == 0) + { + HTTP_Config::instance_ = new HTTP_Config_Info; + + HTTP_Config::instance_->document_root (0); + HTTP_Config::instance_->cgi_path (0); + HTTP_Config::instance_->user_dir (0); + HTTP_Config::instance_->dir_index (0); + HTTP_Config::instance_->proxy_flag (0); + } + + return HTTP_Config::instance_; +} + +HTTP_Config_Info::HTTP_Config_Info (void) + : document_root_ (0), + cgi_path_ (0), + user_dir_ (0), + dir_index_ (0), + proxy_flag_ (0) +{ +} + +HTTP_Config_Info::~HTTP_Config_Info (void) +{ +} + +const char * +HTTP_Config_Info::document_root (void) const +{ + return this->document_root_; +} + +const char * +HTTP_Config_Info::cgi_path (void) const +{ + return this->cgi_path_; +} + +const char * +HTTP_Config_Info::user_dir (void) const +{ + return this->user_dir_; +} + +const char * +HTTP_Config_Info::dir_index (void) const +{ + return this->dir_index_; +} + +int +HTTP_Config_Info::proxy_flag (void) const +{ + return this->proxy_flag_; +} + +const char * +HTTP_Config_Info::document_root (const char *dr_string) +{ + if (dr_string) + this->document_root_ = dr_string; + else + { + this->document_root_ = ACE_OS::getenv ("JAWS_DOCUMENT_ROOT"); + if (!this->document_root_) + this->document_root_ = "."; + } + + return this->document_root_; +} + +const char * +HTTP_Config_Info::cgi_path (const char *cp_string) +{ + if (cp_string) + this->cgi_path_ = cp_string; + else + { + this->cgi_path_ = ACE_OS::getenv ("JAWS_CGI_PATH"); + + if (!this->cgi_path_) + this->cgi_path_ = "cgi-bin"; + } + + return this->cgi_path_; +} + +const char * +HTTP_Config_Info::user_dir (const char *ud_string) +{ + if (ud_string) + this->user_dir_ = ud_string; + else + { + this->user_dir_ = ACE_OS::getenv ("JAWS_USER_DIR"); + if (!this->user_dir_) + this->user_dir_ = ".www"; + } + + return this->user_dir_; +} + +const char * +HTTP_Config_Info::dir_index (const char *di_string) +{ + if (di_string) + this->dir_index_ = di_string; + else + { + this->dir_index_ = ACE_OS::getenv ("JAWS_DIR_INDEX"); + if (!this->dir_index_) + this->dir_index_ = "index.html"; + } + + return this->dir_index_; +} + +int +HTTP_Config_Info::proxy_flag (int pf) +{ + this->proxy_flag_ = pf; + return this->proxy_flag_; +} diff --git a/ACE/apps/JAWS/server/HTTP_Config.h b/ACE/apps/JAWS/server/HTTP_Config.h new file mode 100644 index 00000000000..39c76c44d1b --- /dev/null +++ b/ACE/apps/JAWS/server/HTTP_Config.h @@ -0,0 +1,95 @@ +/* -*- c++ -*- */ +// Hey, Emacs! This is a C++ file! +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// jaws +// +// = FILENAME +// HTTP_Config.h +// +// = AUTHOR +// James Hu +// +// ============================================================================ + +// = Forward declaration. +class HTTP_Config_Info; + +class HTTP_Config + // = TITLE + // Stores server configuration information. + // Someday, this will be hip and cool and be able to parse + // NCSA httpd style config files like Apache does. For now, + // I'm just going to hack in environment variable stuff. + // + // Designed around Singleton pattern. +{ +public: + static HTTP_Config_Info *instance (void); + // Access the Singleton. + +private: + static HTTP_Config_Info *instance_; + // Store the Singleton. +}; + +class HTTP_Config_Info + // = TITLE + // This is where the information is really stored. +{ +friend class HTTP_Config; +public: + HTTP_Config_Info (void); + ~HTTP_Config_Info (void); + + // Accessors to the information + + const char *document_root (void) const; + // Where the root of the document tree begins. This prevents + // clients from being able to examine your entire filesystem. + + const char *cgi_path (void) const; + // A search path for CGI files. + + const char *user_dir (void) const; + // The directory which is appended to a home user directory, e.g., + // ".www-docs" or "public_html". + + const char *dir_index (void) const; + // What is the default index file for a directory, e.g., + // "index.html". + + int proxy_flag (void) const; + // Will the server support proxy requests? + +private: + // = Accesors that can set the data + + const char *document_root (const char *dr_string); + const char *cgi_path (const char *cp_string); + const char *user_dir (const char *ud_string); + const char *dir_index (const char *di_string); + + int proxy_flag (int pf); + +private: + // = Data members + + const char *document_root_; + // The directory root from which documents will be fetched + + const char *cgi_path_; + // The directories from which to expect CGI scripts + + const char *user_dir_; + // Name of the sub-directory where user Web pages are + + const char *dir_index_; + // Name of the Web page to present in place of a directory listing + + int proxy_flag_; + // Should we support proxy requests? Ignored for now. +}; diff --git a/ACE/apps/JAWS/server/HTTP_Handler.cpp b/ACE/apps/JAWS/server/HTTP_Handler.cpp new file mode 100644 index 00000000000..277cfb11f3a --- /dev/null +++ b/ACE/apps/JAWS/server/HTTP_Handler.cpp @@ -0,0 +1,314 @@ +// $Id$ + +// HTTP_Service.cpp -- simple implementation of the HTTP protocol + +#include "ace/Message_Block.h" +#include "ace/Filecache.h" + +#include "HTTP_Handler.h" +#include "HTTP_Helpers.h" +#include "IO.h" +#include "ace/OS_NS_sys_socket.h" +#include "ace/OS_NS_stdio.h" + +ACE_RCSID(server, HTTP_Handler, "$Id$") + +HTTP_Handler::HTTP_Handler (JAWS_IO &io, + HTTP_Handler_Factory &factory) + : factory_ (factory), + request_data_ (0), + handle_ (ACE_INVALID_HANDLE), + response_ (io, request_), + io_ (io) +{ + this->io_.handler (this); +} + +HTTP_Handler::~HTTP_Handler (void) +{ + this->request_data_->release (); + this->request_data_ = 0; +} + +void +HTTP_Handler::open (ACE_HANDLE handle, + ACE_Message_Block &initial_data) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) New connection \n")); + + int sockbufsize = HTTP_Handler::MAX_SOCKBUFSIZE; + int result = ACE_OS::setsockopt (handle, + SOL_SOCKET, + SO_RCVBUF, + (char *) &sockbufsize, + sizeof sockbufsize); + + if (result == -1) + ACE_ERROR ((LM_ERROR, "%p\n", "SO_RCVBUF")); + + sockbufsize = HTTP_Handler::MAX_SOCKBUFSIZE; + + result = ACE_OS::setsockopt (handle, + SOL_SOCKET, + SO_SNDBUF, + (char *) &sockbufsize, + sizeof sockbufsize); + if (result == -1) + ACE_ERROR ((LM_ERROR, "%p\n", "SO_SNDBUF")); + + this->handle_ = handle; + this->io_.handle (this->handle_); + + this->request_data_ = initial_data.duplicate (); + this->read_complete (initial_data); +} + +void +HTTP_Handler::read_complete (ACE_Message_Block &message_block) +{ + // This is actually a callback entry point. The JAWS_IO framework + // calls into this method after some data has been read in. + + switch (this->request_.parse_request (message_block)) + { + case 0: + do + { + int next_read_size + = HTTP_Handler::MAX_REQUEST_SIZE - message_block.length (); + + if (next_read_size == 0) + { + this->request_too_long (); + return; + } + + this->io_.read (message_block, next_read_size); + } + while (0); + break; + + default: + // this->request_.respond (); + this->response_.process_request (); + } +} + +void +HTTP_Handler::receive_file_complete (void) +{ + ACE_DEBUG ((LM_DEBUG, " (%t) %s received successfully\n", + request_.uri ())); + + char buffer[BUFSIZ]; + int buflen = + ACE_OS::sprintf (buffer, + "%s %d %s\r\n", + this->request_.version (), + HTTP_Status_Code::STATUS_OK, + "Successful"); + + this->io_.send_confirmation_message (buffer, buflen); +} + +void +HTTP_Handler::receive_file_error (int result) +{ + ACE_DEBUG ((LM_DEBUG, " (%t) %s error in receiving file\n", + request_.uri ())); + + char buffer[BUFSIZ]; + + int status_code; + switch (result) + { + case ACE_Filecache_Handle::ACE_ACCESS_FAILED: + case ACE_Filecache_Handle::ACE_WRITE_FAILED: + case ACE_Filecache_Handle::ACE_OPEN_FAILED: + status_code = HTTP_Status_Code::STATUS_NOT_FOUND; + break; + case ACE_Filecache_Handle::ACE_COPY_FAILED: + case ACE_Filecache_Handle::ACE_STAT_FAILED: + case ACE_Filecache_Handle::ACE_MEMMAP_FAILED: + status_code = HTTP_Status_Code::STATUS_FORBIDDEN; + break; + default: + status_code = HTTP_Status_Code::STATUS_INTERNAL_SERVER_ERROR; + break; + } + int buflen = + ACE_OS::sprintf (buffer, + "%s %d %s", + this->request_.version (), + status_code, + "Failed"); + + this->io_.send_confirmation_message (buffer, buflen); +} + +void +HTTP_Handler::confirmation_message_complete (void) +{ + this->done (); +} + +void +HTTP_Handler::error_message_complete (void) +{ + this->done (); +} + +void +HTTP_Handler::transmit_file_complete (void) +{ + ACE_DEBUG ((LM_DEBUG, " (%t) %s transmitted successfully\n", + request_.uri ())); + + this->done (); +} + +void +HTTP_Handler::transmit_file_error (int result) +{ + ACE_DEBUG ((LM_DEBUG, + " (%t) %s error in transmitting file\n", + request_.uri ())); + + int status_code; + + switch (result) + { + case ACE_Filecache_Handle::ACE_ACCESS_FAILED: + case ACE_Filecache_Handle::ACE_WRITE_FAILED: + case ACE_Filecache_Handle::ACE_OPEN_FAILED: + status_code = HTTP_Status_Code::STATUS_NOT_FOUND; + break; + case ACE_Filecache_Handle::ACE_COPY_FAILED: + case ACE_Filecache_Handle::ACE_STAT_FAILED: + case ACE_Filecache_Handle::ACE_MEMMAP_FAILED: + status_code = HTTP_Status_Code::STATUS_FORBIDDEN; + break; + default: + status_code = HTTP_Status_Code::STATUS_INTERNAL_SERVER_ERROR; + break; + } + + this->response_.error_response (status_code, "error in transmitting file"); +} + +void +HTTP_Handler::read_error (void) +{ + ACE_DEBUG ((LM_DEBUG, " (%t) error in reading request\n")); + this->done (); +} + +void +HTTP_Handler::write_error (void) +{ + ACE_DEBUG ((LM_DEBUG, " (%t) %s error in writing response\n", + request_.uri ())); + + this->done (); +} + +void +HTTP_Handler::timeout (void) +{ + ACE_DEBUG ((LM_DEBUG, " (%t) %s error in reading request\n", + request_.uri ())); + + this->response_. + error_response (HTTP_Status_Code::STATUS_INTERNAL_SERVER_ERROR, + "error in reading request"); +} + +void +HTTP_Handler::request_too_long (void) +{ + ACE_DEBUG ((LM_DEBUG, " (%t) request too long\n")); + this->response_. + error_response (HTTP_Status_Code::STATUS_BAD_REQUEST, + "request too long"); +} + +void +HTTP_Handler::done (void) +{ + this->factory_.destroy_http_handler (*this, this->io_); +} + +HTTP_Handler_Factory::~HTTP_Handler_Factory (void) +{ +} + +HTTP_Handler * +Synch_HTTP_Handler_Factory::create_http_handler (void) +{ + JAWS_Synch_IO *io; + ACE_NEW_RETURN (io, JAWS_Synch_IO, 0); + HTTP_Handler *handler; + ACE_NEW_RETURN (handler, HTTP_Handler (*io, *this), 0); + + return handler; +} + +void +Synch_HTTP_Handler_Factory::destroy_http_handler (HTTP_Handler &handler, + JAWS_IO &io) +{ + delete &io; + delete &handler; +} + +//-------------SYNCH IO no Cache + +HTTP_Handler * +No_Cache_Synch_HTTP_Handler_Factory::create_http_handler (void) +{ + JAWS_Synch_IO_No_Cache *io; + ACE_NEW_RETURN (io, JAWS_Synch_IO_No_Cache, 0); + HTTP_Handler *handler; + ACE_NEW_RETURN (handler, HTTP_Handler (*io, *this), 0); + + return handler; +} + +void +No_Cache_Synch_HTTP_Handler_Factory::destroy_http_handler (HTTP_Handler &handler, + JAWS_IO &io) +{ + delete &io; + delete &handler; +} + +//---------------- + +// This only works on Win32 +#if defined (ACE_WIN32) +void +Asynch_HTTP_Handler_Factory::open (ACE_HANDLE handle, + ACE_Message_Block &mb) +{ + JAWS_Asynch_IO *io; + ACE_NEW (io, JAWS_Asynch_IO); + HTTP_Handler *handler; + ACE_NEW (handler, HTTP_Handler (*io, *this)); + handler->open (handle, mb); +} + +void +Asynch_HTTP_Handler_Factory::destroy_http_handler (HTTP_Handler &handler, + JAWS_IO &io) +{ + delete &handler; + delete &io; + delete this; +} + +HTTP_Handler * +Asynch_HTTP_Handler_Factory::create_http_handler (void) +{ + return 0; +} +#endif /* ACE_WIN32 */ diff --git a/ACE/apps/JAWS/server/HTTP_Handler.h b/ACE/apps/JAWS/server/HTTP_Handler.h new file mode 100644 index 00000000000..f7eb9b9a693 --- /dev/null +++ b/ACE/apps/JAWS/server/HTTP_Handler.h @@ -0,0 +1,221 @@ +/* -*- c++ -*- */ +// Hey, Emacs! This is a C++ file! +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// jaws +// +// = FILENAME +// HTTP_Handler.h +// +// = AUTHOR +// James Hu and Irfan Pyarali +// +// ============================================================================ + +#ifndef HTTP_HANDLER_H +#define HTTP_HANDLER_H + +// = Forward declarations +class Message_Block; +class HTTP_Handler_Factory; + +#include "ace/Asynch_IO.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "HTTP_Request.h" +#include "HTTP_Response.h" +#include "IO.h" + +class HTTP_Handler : protected JAWS_IO_Handler + // = TITLE + // + // This class is used to implement the HTTP protocol + // + // = DESCRIPTION + // + // The HTTP_Handler class is a state based implementation of the + // HTTP protocol. Therefore, it can be used synchronously and + // asynchronously. It uses an abstract IO class to move between + // different HTTP protocol states. It is up to the IO class to + // decide on synchronous or asynchronous I/O. +{ + // Friend I/O classes. Can call protected methods. + friend class JAWS_Synch_IO; + friend class JAWS_Synch_IO_No_Cache; + friend class JAWS_Asynch_IO; + + // Factories + friend class Asynch_HTTP_Handler_Factory; + friend class Synch_HTTP_Handler_Factory; + friend class No_Cache_Synch_HTTP_Handler_Factory; + +public: + virtual void open (ACE_HANDLE handle, + ACE_Message_Block &initial_data); + // The handler is initialized with a connection <handle> of a new + // client and any <initial_data> that came across. The + // <initial_data> block will be of MAX_REQUEST_SIZE and the number + // of bytes in <initial_data> can be found from + // <initial_data>.length () + +protected: + HTTP_Handler (JAWS_IO &io, + HTTP_Handler_Factory &factory); + // The constructor is passed the factory that created <this> and the + // IO mechanism that the handler should use. + + virtual ~HTTP_Handler (void); + // Destructor + + virtual void timeout (void); + // This method is called by the framework when there is a timeout. + + virtual void done (void); + // This is the termination state of the handler. After successful or + // unsuccessful completions, the handler will end up in this state + // (method). + + virtual void request_too_long (void); + // Request too long. + + HTTP_Handler_Factory &factory_; + // Reference to the creating factory. + +protected: + // = Completion methods inherited from <JAWS_IO_Handler>. + + virtual void read_complete (ACE_Message_Block &data); + virtual void read_error (void); + virtual void transmit_file_complete (void); + virtual void transmit_file_error (int result); + virtual void receive_file_complete (void); + virtual void receive_file_error (int result); + virtual void write_error (void); + virtual void confirmation_message_complete (void); + virtual void error_message_complete (void); + +public: + enum + { + MAX_SOCKBUFSIZE = 64 * 1024, + MAX_REQUEST_SIZE = 8192, + METHODSIZ = 10, + VERSIONSIZ = 10 + }; + +private: + ACE_Message_Block *request_data_; + // This points to the request sent by the client + + ACE_HANDLE handle_; + // I/O handle to the client + + HTTP_Request request_; + HTTP_Response response_; + + JAWS_IO &io_; + // IO class used by the handler +}; + +class HTTP_Handler_Factory + // = TITLE + // + // This class is used to create new HTTP handlers + // + // = DESCRIPTION + // + // This is an abstract factory for creating new HTTP handlers. +{ +public: + virtual ~HTTP_Handler_Factory (void); + // Destructor + + virtual HTTP_Handler *create_http_handler (void) = 0; + // This creates a new HTTP_Handler + + virtual void destroy_http_handler (HTTP_Handler &handler, + JAWS_IO &io) = 0; + // The HTTP handler will call this method from HTTP_Handler::done to + // tell the factory to reap up the handler as it is now done with + // the protocol +}; + +class Synch_HTTP_Handler_Factory : public HTTP_Handler_Factory + // = TITLE + // + // This class is used to create new HTTP handlers that will use + // Synch IO + // + // = DESCRIPTION +{ +public: + HTTP_Handler *create_http_handler (void); + // This creates a new HTTP_Handler + + void destroy_http_handler (HTTP_Handler &handler, + JAWS_IO &io); + // The HTTP handler will call this method from HTTP_Handler::done to + // tell the factory to reap up the handler as it is now done with + // the protocol +}; + +//--------------Added a factory for SYNCH IO without caching + +class No_Cache_Synch_HTTP_Handler_Factory : public HTTP_Handler_Factory + // = TITLE + // + // This class is used to create new HTTP handlers that will use + // Synch IO without caching + // + // = DESCRIPTION +{ +public: + HTTP_Handler *create_http_handler (void); + // This creates a new HTTP_Handler + + void destroy_http_handler (HTTP_Handler &handler, + JAWS_IO &io); + // The HTTP handler will call this method from HTTP_Handler::done to + // tell the factory to reap up the handler as it is now done with + // the protocol +}; + +//-------------- + +#if defined (ACE_WIN32) +class Asynch_HTTP_Handler_Factory : public HTTP_Handler_Factory, public ACE_Service_Handler + // = TITLE + // This class is used to create new HTTP handlers that will use + // Asynchronous IO. This only works on Win32. + // + // = DESCRIPTION +{ +public: + void destroy_http_handler (HTTP_Handler &handler, + JAWS_IO &io); + // The HTTP handler will call this method from HTTP_Handler::done to + // tell the factory to reap up the handler as it is now done with + // the protocol + + virtual void open (ACE_HANDLE handle, + ACE_Message_Block &message_block); + // <open> is called by <ACE_Asynch_Acceptor> to initialize a new + // instance of ACE_Service_Handler that has been created after the a + // new connection is accepted. + // + // This will act as a creation point for new handlers. + +private: + HTTP_Handler *create_http_handler (void); + // This method is private as users are not allowed to create new + // handlers. New handlers can only be created by the framework when + // new client connections arrive. +}; +#endif /* ACE_WIN32 */ +#endif /* HTTP_HANDLER_H */ diff --git a/ACE/apps/JAWS/server/HTTP_Helpers.cpp b/ACE/apps/JAWS/server/HTTP_Helpers.cpp new file mode 100644 index 00000000000..1ca6bfe198d --- /dev/null +++ b/ACE/apps/JAWS/server/HTTP_Helpers.cpp @@ -0,0 +1,442 @@ +// $Id$ + +// HTTP_Helpers.cpp -- Helper utilities for both server and client + +#include "HTTP_Helpers.h" +#include "ace/Log_Msg.h" +#include "ace/OS_NS_string.h" +#include "ace/Guard_T.h" +#include "ace/OS_NS_time.h" +#include "ace/OS_NS_stdio.h" + +ACE_RCSID(server, HTTP_Helpers, "$Id$") + +// = Static initialization. +const char *const +HTTP_Helper::months_[12]= +{ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +char const *HTTP_Helper::alphabet_ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +char * HTTP_Helper::date_string_ = 0; +ACE_SYNCH_MUTEX HTTP_Helper::mutex_; + +ACE_SYNCH_MUTEX HTTP_Status_Code::lock_; +int HTTP_Status_Code::instance_ = 0; +const char *HTTP_Status_Code::Reason[HTTP_Status_Code::MAX_STATUS_CODE + 1]; + +time_t +HTTP_Helper::HTTP_mktime (const char *httpdate) +{ + char *buf; + + ACE_NEW_RETURN (buf, char[ACE_OS::strlen (httpdate) + 1], (time_t) -1); + + // Make spaces in the date be semi-colons so we can parse robustly + // with sscanf. + + const char *ptr1 = httpdate; + char *ptr2 = buf; + + do + { + if (*ptr1 == ' ') + *ptr2++ = ';'; + else + *ptr2++ = *ptr1; + } + while (*ptr1++ != '\0'); + + // In HTTP/1.0, there are three versions of an HTTP_date. + + // rfc1123-date = wkday "," SP dd month yyyy SP hh:mm:ss SP "GMT" + // rfc850-date = weekday "," SP dd-month-yy SP hh:mm:ss SP "GMT" + // asctime-date = wkday SP month dd SP hh:mm:ss SP yyyy + + static const char rfc1123_date[] = "%3s,;%2d;%3s;%4d;%2d:%2d:%2d;GMT"; + static const char rfc850_date[] = "%s,;%2d-%3s-%2d;%2d:%2d:%2d;GMT"; + static const char asctime_date[] = "%3s;%3s;%2d;%2d:%2d:%2d;%4d"; + + // Should also support other versions (such as from NNTP and SMTP) + // for robustness, but it should be clear how to extend this. + + struct tm tms; + char month[4]; + char weekday[10]; + + if (::sscanf(buf, rfc1123_date, + weekday, + &tms.tm_mday, + month, + &tms.tm_year, + &tms.tm_hour, + &tms.tm_min, + &tms.tm_sec) == 7) + ; + else if (::sscanf(buf, rfc850_date, + weekday, + &tms.tm_mday, month, &tms.tm_year, + &tms.tm_hour, &tms.tm_min, &tms.tm_sec) == 7) + { + weekday[3] = '\0'; + } + else if (::sscanf(buf, asctime_date, + weekday, + month, &tms.tm_mday, + &tms.tm_hour, &tms.tm_min, &tms.tm_sec, + &tms.tm_year) == 7) + { + } + + delete [] buf; + + tms.tm_year = HTTP_Helper::fixyear (tms.tm_year); + tms.tm_mon = HTTP_Helper::HTTP_month (month); + + if (tms.tm_mon == -1) + return (time_t) -1; + + // mktime is a Standard C function. + { + +#if !defined (ACE_HAS_REENTRANT_LIBC) + ACE_MT (ACE_Guard<ACE_SYNCH_MUTEX> g (HTTP_Helper::mutex_)); +#endif /* NOT ACE_HAS_REENTRANT_LIBC */ + + return ACE_OS::mktime (&tms); + } +} + +const char * +HTTP_Helper::HTTP_date (void) +{ + if (HTTP_Helper::date_string_ == 0) + { + ACE_MT (ACE_Guard<ACE_SYNCH_MUTEX> m (HTTP_Helper::mutex_)); + + if (HTTP_Helper::date_string_ == 0) + { + // 40 bytes is all I need. + ACE_NEW_RETURN (HTTP_Helper::date_string_, char[40], 0); + + if (!HTTP_Helper::HTTP_date (HTTP_Helper::date_string_)) + { + delete [] HTTP_Helper::date_string_; + HTTP_Helper::date_string_ = 0; + } + } + } + + return HTTP_Helper::date_string_; +} + +const char * +HTTP_Helper::HTTP_date (char *s) +{ + // Return the date-string formatted per HTTP standards. Time must + // be in UTC, so using the 'strftime' call (which obeys the locale) + // isn't correct. + static const char* months[] = {"Jan","Feb","Mar","Apr","May","Jun", + "Jul","Aug","Sep","Oct","Nov","Dec"}; + static const char* days[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; + + time_t tloc; + struct tm tms; + char * date_string = s; + + if (ACE_OS::time (&tloc) != (time_t) -1 + && ACE_OS::gmtime_r (&tloc, &tms) != NULL) + { + ACE_OS::sprintf (date_string, + "%s, %2.2d %s %4.4d %2.2d:%2.2d:%2.2d GMT", + days[tms.tm_wday], tms.tm_mday, months[tms.tm_mon], + tms.tm_year + 1900, tms.tm_hour, tms.tm_min, tms.tm_sec); + } + else + date_string = 0; + + return date_string; +} + +int +HTTP_Helper::HTTP_month (const char *month) +{ + for (size_t i = 0; i < 12; i++) + if (ACE_OS::strcmp(month, HTTP_Helper::months_[i]) == 0) + return i; + + return -1; +} + +const char * +HTTP_Helper::HTTP_month (int month) +{ + if (month < 0 || month >= 12) + return 0; + + return HTTP_Helper::months_[month]; +} + +// Fix the path if it needs fixing/is fixable. + +char * +HTTP_Helper::HTTP_decode_string (char *path) +{ + // replace the percentcodes with the actual character + int i, j; + char percentcode[3]; + + for (i = j = 0; path[i] != '\0'; i++, j++) + { + if (path[i] == '%') + { + percentcode[0] = path[++i]; + percentcode[1] = path[++i]; + percentcode[2] = '\0'; + path[j] = (char) ACE_OS::strtol (percentcode, (char **) 0, 16); + } + else + path[j] = path[i]; + } + + path[j] = path[i]; + + return path; +} + +char * +HTTP_Helper::HTTP_decode_base64 (char *data) +{ + char inalphabet[256], decoder[256]; + + ACE_OS::memset (inalphabet, 0, sizeof (inalphabet)); + ACE_OS::memset (decoder, 0, sizeof (decoder)); + + for (int i = ACE_OS::strlen (HTTP_Helper::alphabet_) - 1; + i >= 0; + i--) + { + inalphabet[(unsigned int) HTTP_Helper::alphabet_[i]] = 1; + decoder[(unsigned int) HTTP_Helper::alphabet_[i]] = i; + } + + char *indata = data; + char *outdata = data; + + int bits = 0; + int c; + int char_count = 0; + int errors = 0; + + while ((c = *indata++) != '\0') + { + if (c == '=') + break; + if (c > 255 || ! inalphabet[c]) + continue; + bits += decoder[c]; + char_count++; + if (char_count == 4) + { + *outdata++ = (bits >> 16); + *outdata++ = ((bits >> 8) & 0xff); + *outdata++ = (bits & 0xff); + bits = 0; + char_count = 0; + } + else + bits <<= 6; + } + + if (c == '\0') + { + if (char_count) + { + ACE_DEBUG ((LM_DEBUG, + "base64 encoding incomplete: at least %d bits truncated\n", + ((4 - char_count) * 6))); + errors++; + } + } + else + { + // c == '=' + switch (char_count) + { + case 1: + ACE_DEBUG ((LM_DEBUG, + "base64 encoding incomplete: at least 2 bits missing\n")); + errors++; + break; + case 2: + *outdata++ = (bits >> 10); + break; + case 3: + *outdata++ = (bits >> 16); + *outdata++ = ((bits >> 8) & 0xff); + break; + } + } + *outdata = '\0'; + return errors ? 0 : data; +} + +char * +HTTP_Helper::HTTP_encode_base64 (char *data) +{ + char buf[BUFSIZ]; + int c; + int error; + int char_count = 0; + int bits = 0; + error = 0; + char *indata = data; + char *outdata = buf; + const unsigned char ASCII_MAX = ~0; + + while ((c = *indata++) != '\0') + { + if (c > (int)ASCII_MAX) + { + ACE_DEBUG ((LM_DEBUG, "encountered char > 255 (decimal %d)\n", c)); + error++; + break; + } + bits += c; + char_count++; + + if (char_count == 3) + { + *outdata++ = HTTP_Helper::alphabet_[bits >> 18]; + *outdata++ = HTTP_Helper::alphabet_[(bits >> 12) & 0x3f]; + *outdata++ = HTTP_Helper::alphabet_[(bits >> 6) & 0x3f]; + *outdata++ = HTTP_Helper::alphabet_[bits & 0x3f]; + bits = 0; + char_count = 0; + } + else + bits <<= 8; + } + + if (!error) + { + if (char_count != 0) + { + bits <<= 16 - (8 * char_count); + *outdata++ = HTTP_Helper::alphabet_[bits >> 18]; + *outdata++ = HTTP_Helper::alphabet_[(bits >> 12) & 0x3f]; + + if (char_count == 1) + { + *outdata++ = '='; + *outdata++ = '='; + } + else + { + *outdata++ = HTTP_Helper::alphabet_[(bits >> 6) & 0x3f]; + *outdata++ = '='; + } + } + *outdata = '\0'; + ACE_OS::strcpy (data, buf); + } + + return (error ? 0 : data); +} + +int +HTTP_Helper::fixyear (int year) +{ + // Fix the year 2000 problem + + if (year > 1000) + year -= 1900; + else if (year < 100) + { + struct tm tms; + time_t tloc; + + if (ACE_OS::time (&tloc) != (time_t) -1) + { + ACE_OS::gmtime_r (&tloc, &tms); + + if (tms.tm_year % 100 == year) + year = tms.tm_year; + + // The last two cases check boundary conditions, in case the + // year just changed at the moment we checked to see if we + // need to fix it. + if ((year+1) % 100 == tms.tm_year % 100) + year = tms.tm_year - 1; + + if (year == (tms.tm_year + 1) % 100) + year = tms.tm_year + 1; + + // What to do if none of the above? + } + } + + return year; +} + +const char ** +HTTP_Status_Code::instance (void) +{ + if (HTTP_Status_Code::instance_ == 0) + { + ACE_MT (ACE_Guard<ACE_SYNCH_MUTEX> g (lock_)); + + if (HTTP_Status_Code::instance_ == 0) + { + for (size_t i = 0; + i < HTTP_Status_Code::MAX_STATUS_CODE + 1; + i++) + { + switch (i) + { + case STATUS_OK: + HTTP_Status_Code::Reason[i] = "OK"; break; + case STATUS_CREATED: + HTTP_Status_Code::Reason[i] = "Created"; break; + case STATUS_ACCEPTED: + HTTP_Status_Code::Reason[i] = "Accepted"; break; + case STATUS_NO_CONTENT: + HTTP_Status_Code::Reason[i] = "No Content"; break; + case STATUS_MOVED_PERMANENTLY: + HTTP_Status_Code::Reason[i] = "Moved Permanently"; break; + case STATUS_MOVED_TEMPORARILY: + HTTP_Status_Code::Reason[i] = "Moved Temporarily"; break; + case STATUS_NOT_MODIFIED: + HTTP_Status_Code::Reason[i] = "Not Modified"; break; + case STATUS_BAD_REQUEST: + HTTP_Status_Code::Reason[i] = "Bad Request"; break; + case STATUS_UNAUTHORIZED: + HTTP_Status_Code::Reason[i] = "Unauthorized"; break; + case STATUS_FORBIDDEN: + HTTP_Status_Code::Reason[i] = "Forbidden"; break; + case STATUS_NOT_FOUND: + HTTP_Status_Code::Reason[i] = "Not Found"; break; + case STATUS_INTERNAL_SERVER_ERROR: + HTTP_Status_Code::Reason[i] = "Internal Server Error"; break; + case STATUS_NOT_IMPLEMENTED: + HTTP_Status_Code::Reason[i] = "Not Implemented"; break; + case STATUS_BAD_GATEWAY: + HTTP_Status_Code::Reason[i] = "Bad Gateway"; break; + case STATUS_SERVICE_UNAVAILABLE: + HTTP_Status_Code::Reason[i] = "Service Unavailable"; break; + default: + HTTP_Status_Code::Reason[i] = "Unknown"; + } + } + + HTTP_Status_Code::instance_ = 1; + } + + // GUARD released + } + + return HTTP_Status_Code::Reason; +} diff --git a/ACE/apps/JAWS/server/HTTP_Helpers.h b/ACE/apps/JAWS/server/HTTP_Helpers.h new file mode 100644 index 00000000000..e7d6eae4f42 --- /dev/null +++ b/ACE/apps/JAWS/server/HTTP_Helpers.h @@ -0,0 +1,109 @@ +/* -*- c++ -*- */ +// Hey, Emacs! This is a C++ file! +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// apps +// +// = FILENAME +// HTTP_Helpers.h +// +// = AUTHOR +// James Hu +// +// ============================================================================ + +#ifndef HTTP_HELPERS_H +#define HTTP_HELPERS_H + +#include "ace/Synch_Traits.h" +#include "ace/Thread_Mutex.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +class HTTP_Helper +// Static functions to enhance the lives of HTTP programmers everywhere. +{ +public: + + // Convert and HTTP-date into a time_t + static time_t HTTP_mktime (const char *httpdate); + + // Create today's date + static const char *HTTP_date (void); + static const char *HTTP_date (char *s); + + // Month conversions (ascii <--> numeric) + static int HTTP_month (const char *month); + static const char *HTTP_month (int month); + + static char *HTTP_decode_string (char *path); + + // Encode/Decode base64 stuff (weak security model) + static char *HTTP_decode_base64 (char *data); + static char *HTTP_encode_base64 (char *data); + +private: + + static int fixyear (int year); + +private: + static const char *const months_[12]; + static char const *alphabet_; + + static char *date_string_; + static ACE_SYNCH_MUTEX mutex_; + // Use this sometimes (e.g. HTTP_date) +}; + +// Design around the Singleton pattern + +class HTTP_Status_Code + // = TITLE + // Go from numeric status codes to descriptive strings. + // + // = DESCRIPTION + // Design around the Singleton pattern +{ +public: + static const char **instance (void); + // Singleton access point. + + enum STATUS_CODE + { + STATUS_OK = 200, + STATUS_CREATED = 201, + STATUS_ACCEPTED = 202, + STATUS_NO_CONTENT = 204, + STATUS_MOVED_PERMANENTLY = 301, + STATUS_MOVED_TEMPORARILY = 302, + STATUS_NOT_MODIFIED = 304, + STATUS_BAD_REQUEST = 400, + STATUS_UNAUTHORIZED = 401, + STATUS_FORBIDDEN = 403, + STATUS_NOT_FOUND = 404, + STATUS_INTERNAL_SERVER_ERROR = 500, + STATUS_NOT_IMPLEMENTED = 501, + STATUS_BAD_GATEWAY = 502, + STATUS_SERVICE_UNAVAILABLE = 503, + STATUS_INSUFFICIENT_DATA = 399 + }; + + enum + { + MAX_STATUS_CODE = 599 + }; + +private: + // Singleton pattern is afoot here. + static const char *Reason[MAX_STATUS_CODE + 1]; + static int instance_; + static ACE_SYNCH_MUTEX lock_; +}; + +#endif /* HTTP_HELPERS_H */ + diff --git a/ACE/apps/JAWS/server/HTTP_Request.cpp b/ACE/apps/JAWS/server/HTTP_Request.cpp new file mode 100644 index 00000000000..c2d4b7a3134 --- /dev/null +++ b/ACE/apps/JAWS/server/HTTP_Request.cpp @@ -0,0 +1,664 @@ +// $Id$ + +#include "ace/Message_Block.h" +#include "HTTP_Request.h" +#include "HTTP_Helpers.h" +#include "HTTP_Config.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_pwd.h" +#include "ace/Log_Msg.h" + +ACE_RCSID(server, HTTP_Request, "$Id$") + +const char *const +HTTP_Request::static_header_strings_[HTTP_Request::NUM_HEADER_STRINGS] = +{ + "Date", + "Pragma", + "Authorization", + "From", + "If-Modified-Since", + "Referrer", + "User-Agent", + "Allow", + "Content-Encoding", + "Content-Length", + "Content-Type", + "Expires", + "Last-Modified" +}; + +const char *const +HTTP_Request::static_method_strings_[HTTP_Request::NUM_METHOD_STRINGS] = +{ + "GET", + "HEAD", + "POST", + "PUT" +}; + +// For reasons of efficiency, this class expects buffer to be +// null-terminated, and buflen does NOT include the \0. + +HTTP_Request::HTTP_Request (void) + : got_request_line_ (0), + method_ (0), + uri_ (0), + version_ (0), + path_ (0), + cgi_ (0), + cgi_env_ (0), + cgi_args_ (0), + query_string_ (0), + path_info_ (0), + header_strings_ (HTTP_Request::static_header_strings_), + method_strings_ (HTTP_Request::static_method_strings_) +{ + + for (size_t i = 0; + i < HTTP_Request::NUM_HEADER_STRINGS; + i++) + this->headers_.recognize (this->header_strings_[i]); +} + +HTTP_Request::~HTTP_Request (void) +{ + ACE_OS::free (this->method_); + ACE_OS::free (this->uri_); + ACE_OS::free (this->version_); + ACE_OS::free (this->path_); + ACE_OS::free (this->query_string_); + ACE_OS::free (this->path_info_); + + delete [] this->cgi_env_; +} + +int +HTTP_Request::parse_request (ACE_Message_Block &mb) +{ + mb.wr_ptr ()[0] = '\0'; + + // Note that RFC 822 does not mention the maximum length of a header + // line. So in theory, there is no maximum length. + + // In Apache, they assume that each header line should not exceed + // 8K. + + int result = this->headers_.complete_header_line (mb.rd_ptr ()); + + if (result != 0) + { + if (!this->got_request_line ()) + { + this->parse_request_line (mb.rd_ptr ()); + while (this->headers_.complete_header_line (mb.rd_ptr ()) > 0) + this->headers_.parse_header_line (mb.rd_ptr ()); + } + else if (result > 0) + do + this->headers_.parse_header_line (mb.rd_ptr ()); + while (this->headers_.complete_header_line (mb.rd_ptr ()) > 0); + } + + mb.wr_ptr (ACE_OS::strlen(mb.rd_ptr ()) - mb.length ()); + + if (this->headers_.end_of_headers () + || (this->got_request_line () && this->version () == 0)) + return this->init (mb.rd_ptr (), mb.length ()); + else + return 0; +} + +void +HTTP_Request::parse_request_line (char *const request_line) +{ + char *ptr = request_line; + char *buf = request_line; + int offset = 1; + + this->status_ = HTTP_Status_Code::STATUS_OK; + + ptr = ACE_OS::strchr (request_line, '\n'); + + if (ptr > request_line && ptr[-1] == '\r') + ptr--, offset++; + + if (ptr == request_line) + { + this->status_ = HTTP_Status_Code::STATUS_BAD_REQUEST; + return; + } + + *ptr = '\0'; + ptr += offset; + + char *lasts = 0; // for strtok_r + + // Get the request type. + this->got_request_line_ = 1; + + if (this->method (ACE_OS::strtok_r (buf, " \t", &lasts)) + && this->uri (ACE_OS::strtok_r (0, " \t", &lasts))) + { + this->type (this->method ()); + + if (this->version (ACE_OS::strtok_r (0, " \t", &lasts)) == 0 + && this->type () != HTTP_Request::GET) + this->status_ = HTTP_Status_Code::STATUS_NOT_IMPLEMENTED; + + if (this->path (this->uri ()) == 0) + this->status_ = HTTP_Status_Code::STATUS_NOT_FOUND; + } + + ACE_DEBUG ((LM_DEBUG, " (%t) request %s %s %s parsed\n", + (this->method () ? this->method () : "-"), + (this->uri () ? this->uri () : "="), + (this->version () ? this->version () : "HTTP/0.9"))); + + ACE_OS::memmove (buf, ptr, ACE_OS::strlen (ptr)+1); +} + +int +HTTP_Request::init (char *const buffer, + int buflen) +{ + // Initialize these every time. + content_length_ = -1; + + // Extract the data pointer. + data_ = buffer; + datalen_ = 0; + + // Set the datalen + if (data_ != 0) + datalen_ = buflen; + else + datalen_ = 0; + + ACE_DEBUG ((LM_DEBUG, " (%t) init has initialized\n")); + + return 1; +} + +const char * +HTTP_Request::method (void) const +{ + return this->method_; +} + +const char * +HTTP_Request::uri (void) const +{ + return this->uri_; +} + +const char * +HTTP_Request::version (void) const +{ + return this->version_; +} + +const char * +HTTP_Request::path (void) const +{ + return this->path_; +} + +int +HTTP_Request::cgi (void) const +{ + return this->cgi_; +} + +const char ** +HTTP_Request::cgi_env (void) const +{ + return (const char **)this->cgi_env_; +} + +const char * +HTTP_Request::cgi_args (void) const +{ + return this->cgi_args_; +} + +const char * +HTTP_Request::query_string (void) const +{ + return this->query_string_; +} + +const char * +HTTP_Request::path_info (void) const +{ + return this->path_info_; +} + +int +HTTP_Request::got_request_line (void) const +{ + return this->got_request_line_; +} + +int +HTTP_Request::type (void) const +{ + return type_; +} + +const Headers & +HTTP_Request::headers (void) const +{ + return this->headers_; +} + +const char * +HTTP_Request::header_strings (int index) const +{ + const char *hs = 0; + + if (0 <= index && index < NUM_HEADER_STRINGS) + hs = this->header_strings_[index]; + + return hs; +} + +const char * +HTTP_Request::header_values (int index) const +{ + const char *hs = 0; + const char *hv = 0; + + if (0 <= index && index < NUM_HEADER_STRINGS) + { + hs = this->header_strings_[index]; + hv = this->headers_[hs].value (); + } + + return hv; +} + +char * +HTTP_Request::data (void) +{ + return data_; +} + +int +HTTP_Request::data_length (void) +{ + return datalen_; +} + +int +HTTP_Request::content_length (void) +{ + if (this->content_length_ == -1) + { + const char * clv = this->headers_["Content-length"].value (); + this->content_length_ = (clv ? ACE_OS::atoi (clv) : 0); + } + + return this->content_length_; +} + +int +HTTP_Request::status (void) +{ + return this->status_; +} + +const char * +HTTP_Request::status_string (void) +{ + return HTTP_Status_Code::instance ()[this->status_]; +} + +void +HTTP_Request::dump (void) +{ + ACE_DEBUG ((LM_DEBUG, "%s command.\n" + "filename is %s," + " length of the file is %d," + " data string is %s," + " datalen is %d," + " status is %d, which is %s\n\n", + this->method () ? this->method () : "EMPTY", + this->uri () ? this->uri () : "EMPTY", + this->content_length (), + this->data () ? this->data () : "EMPTY", + this->data_length (), + this->status (), + this->status_string ())); +} + +const char * +HTTP_Request::method (const char *method_string) +{ + if (this->method_) + ACE_OS::free (this->method_); + + if (method_string == 0) + { + this->status_ = HTTP_Status_Code::STATUS_BAD_REQUEST; + this->method_ = 0; + } + else + this->method_ = ACE_OS::strdup (method_string); + + return this->method_; +} + +const char * +HTTP_Request::uri (char *uri_string) +{ + if (this->uri_) + ACE_OS::free (this->uri_); + + if (uri_string == 0) + { + this->status_ = HTTP_Status_Code::STATUS_BAD_REQUEST; + this->uri_ = 0; + } + else + { + this->uri_ = ACE_OS::strdup (uri_string); + this->cgi (this->uri_); + HTTP_Helper::HTTP_decode_string (this->uri_); + } + + return this->uri_; +} + +const char * +HTTP_Request::version (const char *version_string) +{ + if (this->version_) + ACE_OS::free (this->version_); + + if (version_string) + this->version_ = ACE_OS::strdup (version_string); + else + this->version_ = 0; + + return this->version_; +} + +int +HTTP_Request::type (const char *type_string) +{ + this->type_ = HTTP_Request::NO_TYPE; + + if (type_string == 0) + return this->type_; + + for (size_t i = 0; + i < HTTP_Request::NUM_METHOD_STRINGS; + i++) + + if (ACE_OS::strcmp (type_string, this->method_strings_[i]) == 0) + { + this->type_ = i; + break; + } + + if (this->type_ == HTTP_Request::NO_TYPE) + this->status_ = HTTP_Status_Code::STATUS_NOT_IMPLEMENTED; + + return this->type_; +} + +int +HTTP_Request::cgi (char *uri_string) +{ + this->cgi_ = 0; + this->cgi_env_ = 0; + this->cgi_args_ = 0; + + ACE_DEBUG ((LM_DEBUG, " (%t) HTTP_Request::cgi (%s)\n", uri_string)); + + if (uri_string == 0 || ACE_OS::strlen (uri_string) == 0) + return 0; + + // There are 2 cases where a file could be a CGI script + // + // (1) the file has a CGI extension. + // (2) the file resides in a CGI bin directory. + + char *extra_path_info = 0; + if (this->cgi_in_path (uri_string, extra_path_info) + || this->cgi_in_extension (uri_string, extra_path_info)) + { + cgi_args_and_env (extra_path_info); + + if (extra_path_info) + { + this->path_info_ = ACE_OS::strdup (extra_path_info); + HTTP_Helper::HTTP_decode_string (this->path_info_); + *extra_path_info = '\0'; + } + } + + return this->cgi_; +} + +int +HTTP_Request::cgi_in_path (char *uri_string, char *&extra_path_info) +{ + char *cgi_path; + + ACE_DEBUG ((LM_DEBUG, " (%t) HTTP_Request::cgi_in_path (%s)\n", + uri_string)); + + if (HTTP_Config::instance ()->cgi_path ()) + cgi_path = ACE_OS::strdup (HTTP_Config::instance ()->cgi_path ()); + else + cgi_path = ACE_OS::strdup (""); + + // error checking considered helpful! + if (cgi_path == 0) + return 0; + + char *lasts = 0; + char *cgi_path_next = ACE_OS::strtok_r (cgi_path, ":", &lasts); + + if (cgi_path_next) + do + { + int len = ACE_OS::strlen (cgi_path_next); + + // match path to cgi path + int in_cgi_path = 0; + + if (*cgi_path_next == '/') + { + // cgi path next points to an ``absolute'' path + extra_path_info = uri_string; + in_cgi_path = + (ACE_OS::strncmp (extra_path_info, cgi_path_next, len) == 0); + } + else + { + // cgi path next points to a ``relative'' path + extra_path_info = ACE_OS::strstr (uri_string, cgi_path_next); + in_cgi_path = (extra_path_info != 0); + } + + if (in_cgi_path) + { + if (extra_path_info[len] == '/') + { + this->cgi_ = 1; + extra_path_info += len; + + // move past the executable name + do + extra_path_info++; + while (*extra_path_info != '/' + && *extra_path_info != '?' + && *extra_path_info != '\0'); + + if (*extra_path_info == '\0') + extra_path_info = 0; + + break; + } + } + extra_path_info = 0; + + cgi_path_next = ACE_OS::strtok_r (0, ":", &lasts); + } + while (cgi_path_next); + + ACE_OS::free (cgi_path); + + return this->cgi_; +} + +int +HTTP_Request::cgi_in_extension (char *uri_string, char *&extra_path_info) +{ + extra_path_info = ACE_OS::strstr (uri_string, ".cgi"); + + ACE_DEBUG ((LM_DEBUG, " (%t) HTTP_Request::cgi_in_extension (%s)\n", + uri_string)); + + while (extra_path_info != 0) + { + extra_path_info += 4; + // skip past ``.cgi'' + + switch (*extra_path_info) + { + case '\0': + extra_path_info = 0; + break; + case '/': + case '?': + break; + default: + extra_path_info = ACE_OS::strstr (extra_path_info, ".cgi"); + continue; + } + this->cgi_ = 1; + break; + } + + return this->cgi_; +} + +void +HTTP_Request::cgi_args_and_env (char *&extra_path_info) +{ + char *cgi_question = 0; + + if (extra_path_info) + cgi_question = ACE_OS::strchr (extra_path_info, '?'); + + if (extra_path_info == cgi_question) + extra_path_info = 0; + + if (cgi_question) + { + *cgi_question++ = '\0'; + + if (*cgi_question != '\0') + { + // We need the ``original'' QUERY_STRING for the + // environment. We will substitute '+'s for spaces in the + // other copy. + + this->query_string_ = ACE_OS::strdup (cgi_question); + + char *ptr = cgi_question; + int count = 0; + do + if (*ptr == '+') + *ptr = ' '; + else if (*ptr == '&' || *ptr == '=') + count++; + while (*++ptr); + + count++; + + if (ACE_OS::strchr (cgi_question, '=')) + { + ACE_NEW (this->cgi_env_, char *[count+1]); + + int i = 0; + ptr = cgi_question; + do + { + this->cgi_env_ [i++] = ptr; + + while (*ptr++) + if (*ptr == '&' || *ptr == '=') + *ptr = '\0'; + + HTTP_Helper::HTTP_decode_string (this->cgi_env_[i-1]); + } + while (i < count); + + this->cgi_env_[count] = 0; + } + else + { + this->cgi_args_ = cgi_question; + HTTP_Helper::HTTP_decode_string (cgi_question); + } + } + } +} + +const char * +HTTP_Request::path (const char *uri_string) +{ + char const *file_name = uri_string; + char buf[MAXPATHLEN + 1]; + buf[0] = '\0'; + + if (file_name == 0) return 0; + + if (*file_name == '/') + { + file_name++; + if (*file_name == '~') + { + char *ptr = buf; + + while (*++file_name && *file_name != '/') + *ptr++ = *file_name; + + *ptr = '\0'; + + if (ptr == buf) + ACE_OS::strcpy (buf, ACE_OS::getenv ("HOME")); + else + { +#if !defined (ACE_WIN32) && !defined (VXWORKS) + char pw_buf[BUFSIZ]; + struct passwd pw_struct; + if (ACE_OS::getpwnam_r (buf, &pw_struct, pw_buf, sizeof (pw_buf)) + == 0) + return 0; + ACE_OS::strcpy (buf, pw_struct.pw_dir); +#endif /* NOT ACE_WIN32 AND NOT VXWORKS */ + } + + ACE_OS::strcat (buf, "/"); + ACE_OS::strcat (buf, HTTP_Config::instance ()->user_dir ()); + ACE_OS::strcat (buf, file_name); + } + else + { + // With a starting '/' but no '~' + ACE_OS::strcat (buf, HTTP_Config::instance ()->document_root ()); + ACE_OS::strcat (buf, file_name - 1); + } + } + + if (*buf != '\0') + this->path_ = ACE_OS::strdup (buf); + + return this->path_; +} diff --git a/ACE/apps/JAWS/server/HTTP_Request.h b/ACE/apps/JAWS/server/HTTP_Request.h new file mode 100644 index 00000000000..3581d802540 --- /dev/null +++ b/ACE/apps/JAWS/server/HTTP_Request.h @@ -0,0 +1,202 @@ +/* -*- c++ -*- */ +// Hey, Emacs! This is a C++ file! +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// jaws +// +// = FILENAME +// HTTP_Request.h +// +// = AUTHOR +// James Hu +// +// ============================================================================ + +#ifndef HTTP_REQUEST_H +#define HTTP_REQUEST_H + +#include "ace/config-all.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "Parse_Headers.h" + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL +class ACE_Message_Block; +ACE_END_VERSIONED_NAMESPACE_DECL + +class HTTP_Request + // = TITLE + // This parses the client request of an HTTP transaction. + // + // = DESCRIPTION +{ +public: + HTTP_Request (void); + // Default construction. + + ~HTTP_Request (void); + // Destructor. + + int parse_request (ACE_Message_Block &mb); + // parse an incoming request + + void parse_request_line (char *const request_line); + // the first line of a request is the request line, which is of the + // form: METHOD URI VERSION. + + int init (char *const buffer, + int buflen); + // Initialize the request object. This will parse the buffer and + // prepare for the accessors. + +public: + // = The Accessors. + + const char *method (void) const; + // HTTP request method + + const char *uri (void) const; + // HTTP request uri + + const char *version (void) const; + // HTTP request version + + const char *path (void) const; + // The HTTP request uri translated into a server filename path + + int cgi (void) const; + // TRUE of the request is a cgi request + + const char *cgi_args (void) const; + // The arguments to the cgi request + + const char **cgi_env (void) const; + // The environment variables passed to the CGI request + + const char *query_string (void) const; + // The cgi request query string + + const char *path_info (void) const; + // The cgi request path information + + int type (void) const; + // The type of the HTTP request + + const Headers &headers (void) const; + // The headers that were parsed from the request + + const char *header_strings (int index) const; + // Header strings stored + + const char *header_values (int index) const; + // Values associated with the header strings + + char *data (void); + // The buffer into which request data is read + + int data_length (void); + // The length of the request data + + int content_length (void); + // The length of incoming content if any + + int status (void); + // Current status of the incoming request + + const char *status_string (void); + // A string describing the state of the incoming request + + void dump (void); + // Dump the state of the request. + + enum + { + NO_TYPE = -1, + GET = 0, + HEAD, + POST, + PUT, + NUM_METHOD_STRINGS + }; + // Values for request type + + enum + { + DATE = 0, + PRAGMA, + AUTHORIZATION, + FROM, + IF_MODIFIED_SINCE, + REFERRER, + USER_AGENT, + ALLOW, + CONTENT_ENCODING, + CONTENT_LENGTH, + CONTENT_TYPE, + EXPIRES, + LAST_MODIFIED, + NUM_HEADER_STRINGS + }; + // Header strings + +private: + // = Private Accessors which can set values + const char *method (const char *method_string); + const char *uri (char *uri_string); + const char *version (const char *version_string); + const char *path (const char *uri_string); + + int cgi (char *uri_string); + // determine if the given URI is a CGI program. + + int cgi_in_path (char *uri_string, char *&extra_path_info); + // determine if the given URI resides in a cgi-bin directory + + int cgi_in_extension (char *uri_string, char *&extra_path_info); + // determine if the given URI contains a cgi extension + + void cgi_args_and_env (char *&extra_path_info); + // set the arguments and environment for the cgi program + + int type (const char *type_string); + +private: + int got_request_line (void) const; + +private: + int got_request_line_; + Headers headers_; + + char *method_; + char *uri_; + char *version_; + char *path_; + + int cgi_; + char **cgi_env_; + char *cgi_args_; + + char *query_string_; + char *path_info_; + + const char * const *const header_strings_; + static const char *const static_header_strings_[NUM_HEADER_STRINGS]; + + const char * const *const method_strings_; + static const char *const static_method_strings_[NUM_METHOD_STRINGS]; + + char *data_; + int datalen_; + int content_length_; + char *filename_; + int status_; + int type_; +}; + +#endif /* HTTP_REQUEST_H */ diff --git a/ACE/apps/JAWS/server/HTTP_Response.cpp b/ACE/apps/JAWS/server/HTTP_Response.cpp new file mode 100644 index 00000000000..5f5b036989e --- /dev/null +++ b/ACE/apps/JAWS/server/HTTP_Response.cpp @@ -0,0 +1,388 @@ +// $Id$ + +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" +#include "ace/os_include/os_ctype.h" +#include "ace/Process.h" +#include "ace/Mem_Map.h" +#include "ace/Log_Msg.h" + +#include "HTTP_Response.h" +#include "HTTP_Request.h" +#include "HTTP_Helpers.h" +#include "HTTP_Config.h" +#include "IO.h" + +ACE_RCSID(server, HTTP_Response, "$Id$") + +#if defined (ACE_JAWS_BASELINE) +static char * const EMPTY_HEADER = ""; +#else +static const char * const EMPTY_HEADER = ""; +#endif /* ACE_JAWS_BASELINE */ + +HTTP_Response::HTTP_Response (JAWS_IO &io, HTTP_Request &request) + : io_(io), request_(request) +{ +} + +HTTP_Response::HTTP_Response (HTTP_Request &request, JAWS_IO &io) + : io_(io), request_(request) +{ +} + +HTTP_Response::~HTTP_Response (void) +{ +#if defined (ACE_JAWS_BASELINE) + if (this->HTTP_HEADER != EMPTY_HEADER) + delete [] this->HTTP_HEADER; + // The [] is important. Without it, there was a huge memory leak! +#endif /* ACE_JAWS_BASELINE */ +} + +void +HTTP_Response::process_request(HTTP_Response &response) +{ + response.process_request(); +} + +void +HTTP_Response::process_request (void) +{ + ACE_DEBUG ((LM_DEBUG, " (%t) processing request: %s\n", + this->request_.status_string ())); + + switch (this->request_.status ()) + { + case HTTP_Status_Code::STATUS_OK : + + if (this->request_.cgi ()) + { + this->cgi_response (); + } + else + { + this->normal_response (); + } + + break; + + default: + this->error_response (this->request_.status (), + this->request_.status_string ()); + } +} + +void +HTTP_Response::error_response (int status_code, const char *log_message) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) [%s %s %s] %s\n", + this->request_.method () ? this->request_.method () : "-", + this->request_.uri () ? this->request_.uri () : "-", + this->request_.version() ? this->request_.version () : "-", + log_message ? log_message : "-")); + + static char const error_header1[] = + "%s %d %s\r\n" + "Server: JAWS/1.0prebeta\r\n" + "Content-type: text/html\r\n" + "Content-length: %d\r\n" + "\r\n" + "%s" + ; + + static char const error_header2[] = + "%s %d %s\r\n" + "Server: JAWS/1.0prebeta\r\n" + "WWW-Authenticate: Basic realm=\"JAWS_authorization\"\r\n" + "Content-type: text/html\r\n" + "Content-length: %d\r\n" + "\r\n" + "%s" + ; + + static char const error_message[] = + "<html>\n" + "<head><title>Server error message</title></head>\n" + "<body>\n" + "<h1>Error %d: %s</h1>\n" + "The request could not be completed because:\n %s\n" + "</body>\n" + "</html>\n" + ; + + + char *buf; + char buf1[4 * BUFSIZ]; + char buf2[BUFSIZ]; + + int length; + const char *error_header = error_header1; + + if (status_code == HTTP_Status_Code::STATUS_UNAUTHORIZED) + error_header = error_header2; + + length = + ACE_OS::sprintf (buf2, error_message, + status_code, HTTP_Status_Code::instance ()[status_code], + log_message); + + if (this->request_.version () == 0 + || ACE_OS::strcmp ("HTTP/0.9", this->request_.version ()) == 0) + buf = buf2; + else + { + length = + ACE_OS::sprintf (buf1, error_header, + this->request_.version(), status_code, + HTTP_Status_Code::instance ()[status_code], + length, + buf2); + buf = buf1; + } + + this->io_.send_error_message (buf, length); +} + +void +HTTP_Response::normal_response (void) +{ + const char *hv = 0;; + + ACE_DEBUG ((LM_DEBUG, " (%t) %s request for %s [%s], version %s\n", + request_.method (), request_.uri (), request_.path (), + (request_.version () ? request_.version () : "HTTP/0.9"))); + + switch (this->request_.type ()) + { + case HTTP_Request::GET : + + this->build_headers (); + this->io_.transmit_file (this->request_.path (), + this->HTTP_HEADER, + this->HTTP_HEADER_LENGTH, + this->HTTP_TRAILER, + this->HTTP_TRAILER_LENGTH); + break; + + case HTTP_Request::HEAD : + this->build_headers (); + this->io_.send_confirmation_message (this->HTTP_HEADER, + this->HTTP_HEADER_LENGTH); + break; + + case HTTP_Request::POST : + // What to do here? + // Standard says this is implementation dependent. + // Examples: annotations, page updates, etc. + // This may be a good place to stick CORBA stuff, + // and mobile code. + this->error_response (HTTP_Status_Code::STATUS_NOT_IMPLEMENTED, + "Requested method is not implemented."); + break; + + case HTTP_Request::PUT : + // Only commit to this if we can authenticate it + + // if there is no Authentication: header on the incoming request, + // deny it + hv = this->request_.headers ()["Authorization"].value (); + if (hv == 0 || *hv == '\0') + this->error_response (HTTP_Status_Code::STATUS_UNAUTHORIZED, + "Unauthorized to use PUT method"); + else if (ACE_OS::strncmp (hv, "Basic ", 6) != 0) + // ``6'' is the length of the string "Basic " + this->error_response (HTTP_Status_Code::STATUS_UNAUTHORIZED, + "Unknown authroization method"); + else + { + ACE_Mem_Map mmapfile; + const char *hvv = hv + 6; + // Skip past the string "Basic " + char *buf = new char [ACE_OS::strlen (hv)]; + char *auth + = HTTP_Helper::HTTP_decode_base64 (ACE_OS::strcpy (buf, hvv)); + + if (mmapfile.map (ACE_TEXT ("jaws.auth")) != -1 + && auth != 0 + && ACE_OS::strstr((const char *) mmapfile.addr (), auth) != 0) + this->io_.receive_file (this->request_.path (), + this->request_.data (), + this->request_.data_length (), + this->request_.content_length ()); + else + this->error_response (HTTP_Status_Code::STATUS_UNAUTHORIZED, + "Invalid authorization attempt"); + delete [] buf; + } + break; + + default : + this->error_response (HTTP_Status_Code::STATUS_NOT_IMPLEMENTED, + "Requested method is not implemented."); + } +} + + +void +HTTP_Response::cgi_response (void) +{ + ACE_Process_Options cgi_options; + + if (this->request_.cgi_args ()) + cgi_options.command_line ("%s %s", + this->request_.path (), + this->request_.cgi_args ()); + else + cgi_options.command_line ("%s", this->request_.path ()); + + // Build environment variables + cgi_options.setenv ("SERVER_SOFTWARE", "%s", "JAWS/1.0"); + cgi_options.setenv ("SERVER_NAME", "%s", "localhost"); + cgi_options.setenv ("GATEWAY_INTERFACE", "%s", "CGI/1.1"); + + cgi_options.setenv ("SERVER_PROTOCOL", "%s", + this->request_.version () + ? this->request_.version () + : "HTTP/0.9"); + cgi_options.setenv ("SERVER_PORT", "%d", 5432); + + cgi_options.setenv ("REQUEST_METHOD", "%s", this->request_.method ()); + + if (this->request_.path_info ()) + { + cgi_options.setenv ("PATH_INFO", "%s", + this->request_.path_info ()); + cgi_options.setenv ("PATH_TRANSLATED", + "%s/%s", + HTTP_Config::instance ()->document_root (), + this->request_.path_info ()); + } + + cgi_options.setenv ("SCRIPT_NAME", + "%s", + this->request_.uri ()); + + if (this->request_.query_string ()) + cgi_options.setenv ("QUERY_STRING", + "%s", + this->request_.query_string ()); + + if (this->request_.cgi_env ()) + for (size_t i = 0; this->request_.cgi_env ()[i]; i += 2) + cgi_options.setenv (this->request_.cgi_env ()[i], + "%s", + this->request_.cgi_env ()[i+1]); + + char buf[BUFSIZ]; + char *p, *q; + ACE_OS::strcpy (buf, "HTTP_"); + p = q = buf + ACE_OS::strlen (buf); + + for (size_t i = 0; i < HTTP_Request::NUM_HEADER_STRINGS; i++) + { + int j = 0; + + for (char c; (c = this->request_.header_strings (i)[j++]) != '\0'; ) + if (isalpha (c)) + *q++ = toupper (c); + else if (c == '-') + *q++ = '_'; + else + *q++ = c; + + *q = '\0'; + + const char *hv = this->request_.header_values (i); + + if (hv && *hv) + cgi_options.setenv (buf, "%s", hv); + q = p; + } + + cgi_options.set_handles (this->io_.handle (), + this->io_.handle (), + this->io_.handle ()); + + this->build_headers (); + this->io_.send_confirmation_message (this->HTTP_HEADER, + this->HTTP_HEADER_LENGTH); + // ACE::send (this->io_.handle (), + // this->HTTP_HEADER, this->HTTP_HEADER_LENGTH); + + // Exec the CGI program. + ACE_Process cgi_process; + cgi_process.spawn (cgi_options); + // cgi_process.wait (); +} + +void +HTTP_Response::build_headers (void) +{ + // At this point, we should really determine the type of request + // this is, and build the appropriate header. + + // Let's assume this is HTML for now. Unless the request is CGI, + // then do not include content-* headers. + + if (this->request_.version () == 0 + || ACE_OS::strcmp ("HTTP/0.9", this->request_.version ()) == 0) + { + HTTP_HEADER = EMPTY_HEADER; + HTTP_HEADER_LENGTH = 0; + } + else + { +#if defined (ACE_JAWS_BASELINE) + HTTP_HEADER = new char[BUFSIZ * 4]; + + // We assume that at this point everything is OK + HTTP_HEADER_LENGTH = + ACE_OS::sprintf (HTTP_HEADER, "%s", "HTTP/1.0 200 OK\r\n"); + + char date_ptr [40]; + // 40 bytes is the maximum length needed to store the date + + if (HTTP_Helper::HTTP_date (date_ptr) != 0) + HTTP_HEADER_LENGTH += + ACE_OS::sprintf (HTTP_HEADER+HTTP_HEADER_LENGTH, + "Date: %s\r\n", date_ptr); + + if (! this->request_.cgi ()) { + HTTP_HEADER_LENGTH += + ACE_OS::sprintf (HTTP_HEADER+HTTP_HEADER_LENGTH, + "Content-type: %s\r\n", + "text/html"); + + struct stat file_stat; + // If possible, add the Content-length field to the header. + // @@ Note that using 'ACE_OS::stat' is a hack. Normally, a + // web browser will have a 'virtual' file system. In a VFS, + // 'stat' might not reference the correct location. + if ((this->request_.type () == HTTP_Request::GET) && + (ACE_OS::stat (this->request_.path (), &file_stat) == 0)) + { + HTTP_HEADER_LENGTH += + ACE_OS::sprintf (HTTP_HEADER+HTTP_HEADER_LENGTH, + "Content-length: %u\r\n", file_stat.st_size); + } + + // Complete header with empty line and adjust header length. + HTTP_HEADER[HTTP_HEADER_LENGTH++] = '\r'; + HTTP_HEADER[HTTP_HEADER_LENGTH++] = '\n'; + } +#else + if (! this->request_.cgi ()) + HTTP_HEADER = "HTTP/1.0 200 OK\r\n" + "Content-type: text/html\r\n\r\n"; + else + HTTP_HEADER = "HTTP/1.0 200 OK\r\n"; + + HTTP_HEADER_LENGTH = ACE_OS::strlen (HTTP_HEADER); + +#endif /* ACE_JAWS_BASELINE */ + } + + HTTP_TRAILER = ""; + HTTP_TRAILER_LENGTH = 0; +} diff --git a/ACE/apps/JAWS/server/HTTP_Response.h b/ACE/apps/JAWS/server/HTTP_Response.h new file mode 100644 index 00000000000..2bd8d21fbc0 --- /dev/null +++ b/ACE/apps/JAWS/server/HTTP_Response.h @@ -0,0 +1,80 @@ +/* -*- c++ -*- */ +// Hey, Emacs! This is a C++ file! +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// jaws +// +// = FILENAME +// HTTP_Response.h +// +// = AUTHOR +// James Hu +// +// ============================================================================ + +#ifndef HTTP_RESPONSE_H +#define HTTP_RESPONSE_H + +class JAWS_IO; +class HTTP_Request; + +class HTTP_Response + // = TITLE + // Abstraction for HTTP responses. + // + // = DESCRIPTION + // Provides an encapsulation of responses to HTTP requests. + // For instance, given an HTTP GET request, it will produce + // header and body suitable for returning to the client who made + // the request. +{ +public: + HTTP_Response (JAWS_IO &io, + HTTP_Request &request); + HTTP_Response (HTTP_Request &request, JAWS_IO &io); + ~HTTP_Response (void); + + void process_request (void); + // This is called by the handler to initiate a response. + + void error_response (int status, + const char *log_message); + // This returns an error response for cases where there is a problem + // with the request, logging the log_message. + +private: + + void normal_response (void); + // Called by process_request when the request is a normal request. + + void cgi_response (void); + // Called by process_request when the request is a cgi request. + +private: + + static void process_request (HTTP_Response &response); + // static version of process_request, just in case. + + void build_headers (void); + // creates the appropriate header information for responses. + +private: + JAWS_IO &io_; + HTTP_Request &request_; + // The IO and Request objects associated with this re + +#if defined (ACE_JAWS_BASELINE) + char *HTTP_HEADER; +#else + const char *HTTP_HEADER; +#endif + const char *HTTP_TRAILER; + int HTTP_HEADER_LENGTH; + int HTTP_TRAILER_LENGTH; + // HTTP Headers and trailers. +}; + +#endif /* HTTP_RESPONSE_H */ diff --git a/ACE/apps/JAWS/server/HTTP_Server.cpp b/ACE/apps/JAWS/server/HTTP_Server.cpp new file mode 100644 index 00000000000..86ffea0542a --- /dev/null +++ b/ACE/apps/JAWS/server/HTTP_Server.cpp @@ -0,0 +1,431 @@ +// $Id$ + +#ifndef ACE_BUILD_SVC_DLL +#define ACE_BUILD_SVC_DLL +#endif /* ACE_BUILD_SVC_DLL */ + +#include "ace/OS_NS_string.h" +#include "ace/Get_Opt.h" +#include "ace/Asynch_Acceptor.h" +#include "ace/LOCK_SOCK_Acceptor.h" +#include "ace/Proactor.h" +#include "ace/Signal.h" +#include "ace/Auto_Ptr.h" + +#include "IO.h" +#include "HTTP_Server.h" + +ACE_RCSID(server, HTTP_Server, "$Id$") + +// class is overkill +class JAWS +{ +public: + enum + { + JAWS_POOL = 0, + JAWS_PER_REQUEST = 1 + }; + + enum + { + JAWS_SYNCH = 0, + JAWS_ASYNCH = 2 + }; +}; + +void +HTTP_Server::parse_args (int argc, ACE_TCHAR *argv[]) +{ + int c; + int thr_strategy = 0; + int io_strategy = 0; + const ACE_TCHAR *prog = argc > 0 ? argv[0] : ACE_TEXT ("HTTP_Server"); + + // Set some defaults + this->port_ = 0; + this->threads_ = 0; + this->backlog_ = 0; + this->throttle_ = 0; + this->caching_ = true; + + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("p:n:t:i:b:c:")); + + while ((c = get_opt ()) != -1) + switch (c) + { + case 'p': + this->port_ = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'n': + this->threads_ = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 't': + // POOL -> thread pool + // PER_REQUEST -> thread per request + // THROTTLE -> thread per request with throttling + if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("POOL")) == 0) + thr_strategy = JAWS::JAWS_POOL; + else if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("PER_REQUEST")) == 0) + { + thr_strategy = JAWS::JAWS_PER_REQUEST; + this->throttle_ = 0; + } + else if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("THROTTLE")) == 0) + { + thr_strategy = JAWS::JAWS_PER_REQUEST; + this->throttle_ = 1; + } + break; + case 'f': + if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("THR_BOUND")) == 0) + { + // What happened here? + } + else if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("THR_DAEMON")) == 0) + { + } + else if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("THR_DETACHED")) == 0) + { + } + case 'i': + // SYNCH -> synchronous I/O + // ASYNCH -> asynchronous I/O + if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("SYNCH")) == 0) + io_strategy = JAWS::JAWS_SYNCH; + else if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("ASYNCH")) == 0) + io_strategy = JAWS::JAWS_ASYNCH; + break; + case 'b': + this->backlog_ = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'c': + if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("NO_CACHE")) == 0) + this->caching_ = false; + else + this->caching_ = true; + break; + default: + break; + } + + // No magic numbers. + if (this->port_ <= 0) + this->port_ = 5432; + if (this->threads_ <= 0) + this->threads_ = 5; + // Don't use number of threads as default + if (this->backlog_ <= 0) + this->backlog_ = this->threads_; + + this->strategy_ = thr_strategy | io_strategy; + + ACE_UNUSED_ARG (prog); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("in HTTP_Server::init, %s port = %d, ") + ACE_TEXT ("number of threads = %d\n"), + prog, this->port_, this->threads_)); +} + +int +HTTP_Server::init (int argc, ACE_TCHAR *argv[]) + // Document this function +{ + // Ignore signals generated when a connection is broken unexpectedly. + ACE_Sig_Action sig ((ACE_SignalHandler) SIG_IGN, SIGPIPE); + ACE_UNUSED_ARG (sig); + + // Parse arguments which sets the initial state. + this->parse_args (argc, argv); + + //If the IO strategy is synchronous (SYNCH case), then choose a handler + //factory based on the desired caching scheme + HTTP_Handler_Factory *f = 0; + + if (this->strategy_ != (JAWS::JAWS_POOL | JAWS::JAWS_ASYNCH)) + if (this->caching_) + ACE_NEW_RETURN (f, Synch_HTTP_Handler_Factory (), -1); + else + ACE_NEW_RETURN (f, No_Cache_Synch_HTTP_Handler_Factory (), -1); + + //NOTE: At this point f better not be a NULL pointer, + //so please do not change the ACE_NEW_RETURN macros unless + //you know what you are doing + ACE_Auto_Ptr<HTTP_Handler_Factory> factory (f); + + + // Choose what concurrency strategy to run. + switch (this->strategy_) + { + case (JAWS::JAWS_POOL | JAWS::JAWS_ASYNCH) : + return this->asynch_thread_pool (); + + case (JAWS::JAWS_PER_REQUEST | JAWS::JAWS_SYNCH) : + return this->thread_per_request (*factory.get ()); + + case (JAWS::JAWS_POOL | JAWS::JAWS_SYNCH) : + default: + return this->synch_thread_pool (*factory.get ()); + } + + ACE_NOTREACHED (return 0); +} + +int +HTTP_Server::fini (void) +{ + this->tm_.close (); + return 0; +} + + +int +HTTP_Server::synch_thread_pool (HTTP_Handler_Factory &factory) +{ + // Main thread opens the acceptor + if (this->acceptor_.open (ACE_INET_Addr (this->port_), 1, + PF_INET, this->backlog_) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("HTTP_Acceptor::open")), -1); + + // Create a pool of threads to handle incoming connections. + Synch_Thread_Pool_Task t (this->acceptor_, this->tm_, this->threads_, factory); + + this->tm_.wait (); + return 0; +} + +Synch_Thread_Pool_Task::Synch_Thread_Pool_Task (HTTP_Acceptor &acceptor, + ACE_Thread_Manager &tm, + int threads, + HTTP_Handler_Factory &factory) + : ACE_Task<ACE_NULL_SYNCH> (&tm), + acceptor_ (acceptor), + factory_ (factory) +{ + if (this->activate (THR_DETACHED | THR_NEW_LWP, threads) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("Synch_Thread_Pool_Task::open"))); +} + +int +Synch_Thread_Pool_Task::svc (void) +{ + // Creates a factory of HTTP_Handlers binding to synchronous I/O strategy + //Synch_HTTP_Handler_Factory factory; + + for (;;) + { + ACE_SOCK_Stream stream; + + // Lock in this accept. When it returns, we have a connection. + if (this->acceptor_.accept (stream) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT("%p\n"), + ACE_TEXT ("HTTP_Acceptor::accept")), -1); + + ACE_Message_Block *mb; + ACE_NEW_RETURN (mb, + ACE_Message_Block (HTTP_Handler::MAX_REQUEST_SIZE + 1), + -1); + + // Create an HTTP Handler to handle this request + HTTP_Handler *handler = this->factory_.create_http_handler (); + handler->open (stream.get_handle (), *mb); + // Handler is destroyed when the I/O puts the Handler into the + // done state. + + mb->release (); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) in Synch_Thread_Pool_Task::svc, recycling\n"))); + } + + ACE_NOTREACHED(return 0); +} + +int +HTTP_Server::thread_per_request (HTTP_Handler_Factory &factory) +{ + int grp_id = -1; + + // thread per request + // Main thread opens the acceptor + if (this->acceptor_.open (ACE_INET_Addr (this->port_), 1, + PF_INET, this->backlog_) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("HTTP_Acceptor::open")), -1); + + ACE_SOCK_Stream stream; + + // When we are throttling, this is the amount of time to wait before + // checking for runnability again. + const ACE_Time_Value wait_time (0, 10); + + for (;;) + { + if (this->acceptor_.accept (stream) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("HTTP_Acceptor::accept")), -1); + + Thread_Per_Request_Task *t; + // Pass grp_id as a constructor param instead of into open. + ACE_NEW_RETURN (t, Thread_Per_Request_Task (stream.get_handle (), + this->tm_, + grp_id, + factory), + -1); + + + if (t->open () != 0) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("Thread_Per_Request_Task::open")), + -1); + + // Throttling is not allowing too many threads to run away. + // Should really use some sort of condition variable here. + if (!this->throttle_) + continue; + + // This works because each task has only one thread. + while (this->tm_.num_tasks_in_group (grp_id) > this->threads_) + this->tm_.wait (&wait_time); + } + + ACE_NOTREACHED(return 0); +} + +Thread_Per_Request_Task::Thread_Per_Request_Task (ACE_HANDLE handle, + ACE_Thread_Manager &tm, + int &grp_id, + HTTP_Handler_Factory &factory) + : ACE_Task<ACE_NULL_SYNCH> (&tm), + handle_ (handle), + grp_id_ (grp_id), + factory_ (factory) +{ +} + + +// HEY! Add a method to the thread_manager to return total number of +// threads managed in all the tasks. + +int +Thread_Per_Request_Task::open (void *) +{ + int status = -1; + + if (this->grp_id_ == -1) + status = this->grp_id_ = this->activate (THR_DETACHED | THR_NEW_LWP); + else + status = this->activate (THR_DETACHED | THR_NEW_LWP, + 1, 0, -1, this->grp_id_, 0); + + if (status == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("Thread_Per_Request_Task::open")), + -1); + return 0; +} + +int +Thread_Per_Request_Task::svc (void) +{ + ACE_Message_Block *mb; + ACE_NEW_RETURN (mb, ACE_Message_Block (HTTP_Handler::MAX_REQUEST_SIZE + 1), + -1); + //Synch_HTTP_Handler_Factory factory; + HTTP_Handler *handler = this->factory_.create_http_handler (); + handler->open (this->handle_, *mb); + mb->release (); + return 0; +} + +int +Thread_Per_Request_Task::close (u_long) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) Thread_Per_Request_Task::svc, dying\n"))); + delete this; + return 0; +} + +// Understanding the code below requires understanding of the +// WindowsNT asynchronous completion notification mechanism and the +// Proactor Pattern. + +// (1) The application submits an asynchronous I/O request to the +// operating system and a special handle with it (Asynchronous +// Completion Token). +// (2) The operating system commits to performing the I/O request, +// while application does its own thing. +// (3) Operating system finishes the I/O request and places ACT onto +// the I/O Completion Port, which is a queue of finished +// asynchronous requests. +// (4) The application eventually checks to see if the I/O request +// is done by checking the I/O Completion Port, and retrieves the +// ACT. + +int +HTTP_Server::asynch_thread_pool (void) +{ +// This only works on Win32 +#if defined (ACE_WIN32) + // Create the appropriate acceptor for this concurrency strategy and + // an appropriate handler for this I/O strategy + ACE_Asynch_Acceptor<Asynch_HTTP_Handler_Factory> acceptor; + + // Tell the acceptor to listen on this->port_, which makes an + // asynchronous I/O request to the OS. + if (acceptor.open (ACE_INET_Addr (this->port_), + HTTP_Handler::MAX_REQUEST_SIZE + 1) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("ACE_Asynch_Acceptor::open")), -1); + + // Create the thread pool. + // Register threads with the proactor and thread manager. + Asynch_Thread_Pool_Task t (*ACE_Proactor::instance (), + this->tm_); + + // The proactor threads are waiting on the I/O Completion Port. + + // Wait for the threads to finish. + return this->tm_.wait (); +#endif /* ACE_WIN32 */ + return -1; +} + +// This only works on Win32 +#if defined (ACE_WIN32) + +Asynch_Thread_Pool_Task::Asynch_Thread_Pool_Task (ACE_Proactor &proactor, + ACE_Thread_Manager &tm) + : ACE_Task<ACE_NULL_SYNCH> (&tm), + proactor_ (proactor) +{ + if (this->activate () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("Asynch_Thread_Pool_Task::open"))); +} + +int +Asynch_Thread_Pool_Task::svc (void) +{ + for (;;) + if (this->proactor_.handle_events () == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("ACE_Proactor::handle_events")), + -1); + + return 0; +} + +#endif /* ACE_WIN32 */ + +// Define the factory function. +ACE_SVC_FACTORY_DEFINE (HTTP_Server) + +// Define the object that describes the service. +ACE_STATIC_SVC_DEFINE (HTTP_Server, ACE_TEXT ("HTTP_Server"), ACE_SVC_OBJ_T, + &ACE_SVC_NAME (HTTP_Server), + ACE_Service_Type::DELETE_THIS + | ACE_Service_Type::DELETE_OBJ, 0) + diff --git a/ACE/apps/JAWS/server/HTTP_Server.h b/ACE/apps/JAWS/server/HTTP_Server.h new file mode 100644 index 00000000000..9e8534c4cbd --- /dev/null +++ b/ACE/apps/JAWS/server/HTTP_Server.h @@ -0,0 +1,155 @@ +// -*- C++ -*- +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// jaws +// +// = FILENAME +// HTTP_Server.h +// +// = AUTHOR +// James Hu +// +// ============================================================================ + +#ifndef HTTP_SERVER_H +#define HTTP_SERVER_H + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Service_Config.h" +#include "ace/Service_Object.h" +#include "ace/Thread_Manager.h" +#include "ace/Acceptor.h" +#include "ace/LOCK_SOCK_Acceptor.h" +#include "ace/Task_T.h" +#include "ace/Asynch_IO.h" +#include "ace/svc_export.h" +#include "HTTP_Handler.h" +#include "ace/Synch_Traits.h" +#include "ace/Thread_Mutex.h" +#include "ace/Null_Mutex.h" +#include "ace/Global_Macros.h" + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL +// Forward declaration. +class ACE_Proactor; +ACE_END_VERSIONED_NAMESPACE_DECL + +#if defined (ACE_HAS_THREAD_SAFE_ACCEPT) +typedef ACE_LOCK_SOCK_Acceptor<ACE_SYNCH_NULL_MUTEX> HTTP_SOCK_Acceptor; +#else +typedef ACE_LOCK_SOCK_Acceptor<ACE_SYNCH_MUTEX> HTTP_SOCK_Acceptor; +#endif /* ACE_HAS_THREAD_SAFE_ACCEPT */ + +typedef HTTP_SOCK_Acceptor HTTP_Acceptor; + +class ACE_Svc_Export HTTP_Server : public ACE_Service_Object + // = TITLE + // This server is used to create HTTP Handlers for the Web + // server + // + // = DESCRIPTION +{ +public: + virtual int init (int argc, ACE_TCHAR *argv[]); + // Initialization + + virtual int fini (void); + // Exit hooks + +protected: + virtual int thread_per_request (HTTP_Handler_Factory &factory); + // Thread Per Request implementation + + virtual int asynch_thread_pool (void); + // Asynch Thread Pool implementation + + virtual int synch_thread_pool (HTTP_Handler_Factory &factory); + // Synch Thread Pool implementation + +private: + // James, comment these data members. + void parse_args (int argc, ACE_TCHAR **argv); + int port_; + int threads_; + int strategy_; + int backlog_; + int throttle_; + bool caching_; + ACE_Thread_Manager tm_; + HTTP_Acceptor acceptor_; +}; + +class Synch_Thread_Pool_Task : public ACE_Task<ACE_NULL_SYNCH> + // = TITLE + // Used to implement Synch Thread Pool + // + // = DESCRIPTION + // Describe this and the others below. + // NOTE: this class was modified to make caching disabling possible +{ +public: + Synch_Thread_Pool_Task (HTTP_Acceptor &acceptor, + ACE_Thread_Manager &tm, + int threads, + HTTP_Handler_Factory &factory); + virtual int svc (void); + +private: + HTTP_Acceptor &acceptor_; + HTTP_Handler_Factory &factory_; +}; + +class Thread_Per_Request_Task : public ACE_Task<ACE_NULL_SYNCH> + // = TITLE + // Used to implement Thread Per Request. + // + // = DESCRIPTION + // Spawns a new thread for every new incoming connection. The + // handle below is the socket stream of the incoming connection. + // NOTE: this class was modified to make caching disabling possible +{ +public: + Thread_Per_Request_Task (ACE_HANDLE handle, + ACE_Thread_Manager &tm, + int &grp_id, + HTTP_Handler_Factory &factory); + virtual int open (void *args = 0); + virtual int close (u_long); + virtual int svc (void); + +private: + ACE_HANDLE handle_; + int &grp_id_; + HTTP_Handler_Factory &factory_; +}; + +// This only works on Win32 +#if defined (ACE_WIN32) +class Asynch_Thread_Pool_Task : public ACE_Task<ACE_NULL_SYNCH> + // = TITLE + // Used to implement Asynch Thread Pool + // + // = DESCRIPTION + // The proactor below utilizes WaitForMultipleObjects. +{ +public: + Asynch_Thread_Pool_Task (ACE_Proactor &proactor, + ACE_Thread_Manager &tm); + virtual int svc (void); + +private: + ACE_Proactor &proactor_; +}; +#endif /* ACE_WIN32 */ + +ACE_SVC_FACTORY_DECLARE (HTTP_Server) + +ACE_STATIC_SVC_DECLARE_EXPORT (ACE_Svc, HTTP_Server) + +#endif /* HTTP_SERVER_H */ diff --git a/ACE/apps/JAWS/server/IO.cpp b/ACE/apps/JAWS/server/IO.cpp new file mode 100644 index 00000000000..e08b7d89f64 --- /dev/null +++ b/ACE/apps/JAWS/server/IO.cpp @@ -0,0 +1,570 @@ +// $Id$ + +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_sys_uio.h" +#include "ace/OS_NS_sys_socket.h" +#include "ace/Message_Block.h" +#include "ace/Min_Max.h" +#include "ace/SOCK_Stream.h" +#include "ace/Filecache.h" +#include "IO.h" +#include "HTTP_Helpers.h" + +#include "ace/OS_NS_fcntl.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_sys_stat.h" +#include "ace/Auto_Ptr.h" + +ACE_RCSID (server, + IO, + "$Id$") + + +JAWS_IO::JAWS_IO (void) + : handler_ (0) +{ +} + +JAWS_IO::~JAWS_IO (void) +{ +} + +void +JAWS_IO::handler (JAWS_IO_Handler *handler) +{ + this->handler_ = handler; +} + +JAWS_IO_Handler::~JAWS_IO_Handler (void) +{ +} + +JAWS_Synch_IO::JAWS_Synch_IO (void) + : handle_ (ACE_INVALID_HANDLE) +{ +} + +JAWS_Synch_IO::~JAWS_Synch_IO (void) +{ + ACE_OS::closesocket (this->handle_); +} + +ACE_HANDLE +JAWS_Synch_IO::handle (void) const +{ + return this->handle_; +} + +void +JAWS_Synch_IO::handle (ACE_HANDLE handle) +{ + this->handle_ = handle; +} + +void +JAWS_Synch_IO::read (ACE_Message_Block &mb, + int size) +{ + ACE_SOCK_Stream stream; + stream.set_handle (this->handle_); + int result = stream.recv (mb.wr_ptr (), size); + + if (result <= 0) + this->handler_->read_error (); + else + { + mb.wr_ptr (result); + this->handler_->read_complete (mb); + } +} + +void +JAWS_Synch_IO::receive_file (const char *filename, + void *initial_data, + int initial_data_length, + int entire_length) +{ + ACE_Filecache_Handle handle (filename, entire_length); + + int result = handle.error (); + + if (result == ACE_Filecache_Handle::ACE_SUCCESS) + { + ACE_SOCK_Stream stream; + stream.set_handle (this->handle_); + + int bytes_to_memcpy = ACE_MIN (entire_length, initial_data_length); + ACE_OS::memcpy (handle.address (), initial_data, bytes_to_memcpy); + + int bytes_to_read = entire_length - bytes_to_memcpy; + + int bytes = stream.recv_n ((char *) handle.address () + initial_data_length, + bytes_to_read); + if (bytes == bytes_to_read) + this->handler_->receive_file_complete (); + else + result = -1; + } + + if (result != ACE_Filecache_Handle::ACE_SUCCESS) + this->handler_->receive_file_error (result); +} + +void +JAWS_Synch_IO::transmit_file (const char *filename, + const char *header, + int header_size, + const char *trailer, + int trailer_size) +{ + ACE_Filecache_Handle handle (filename); + + int result = handle.error (); + + if (result == ACE_Filecache_Handle::ACE_SUCCESS) + { +#if defined (ACE_JAWS_BASELINE) || defined (ACE_WIN32) + ACE_SOCK_Stream stream; + stream.set_handle (this->handle_); + + if ((stream.send_n (header, header_size) == header_size) + && (stream.send_n (handle.address (), handle.size ()) + == handle.size ()) + && (stream.send_n (trailer, trailer_size) == trailer_size)) + this->handler_->transmit_file_complete (); + else + result = -1; +#else + // Attempting to use writev + // Is this faster? + iovec iov[3]; + int iovcnt = 0; + if (header_size > 0) + { + iov[iovcnt].iov_base = const_cast<char*> (header); + iov[iovcnt].iov_len = header_size; + iovcnt++; + } + if (handle.size () > 0) + { + iov[iovcnt].iov_base = reinterpret_cast<char*> (handle.address ()); + iov[iovcnt].iov_len = handle.size (); + iovcnt++; + } + if (trailer_size > 0) + { + iov[iovcnt].iov_base = const_cast<char*> (trailer); + iov[iovcnt].iov_len = trailer_size; + iovcnt++; + } + if (ACE_OS::writev (this->handle_, iov, iovcnt) < 0) + result = -1; + else + this->handler_->transmit_file_complete (); +#endif /* ACE_JAWS_BASELINE */ + } + + if (result != ACE_Filecache_Handle::ACE_SUCCESS) + this->handler_->transmit_file_error (result); +} + +void +JAWS_Synch_IO::send_confirmation_message (const char *buffer, + int length) +{ + this->send_message (buffer, length); + this->handler_->confirmation_message_complete (); +} + +void +JAWS_Synch_IO::send_error_message (const char *buffer, + int length) +{ + this->send_message (buffer, length); + this->handler_->error_message_complete (); +} + +void +JAWS_Synch_IO::send_message (const char *buffer, + int length) +{ + ACE_SOCK_Stream stream; + stream.set_handle (this->handle_); + stream.send_n (buffer, length); +} + +// This only works on Win32 +#if defined (ACE_WIN32) + +JAWS_Asynch_IO::JAWS_Asynch_IO (void) +{ +} + +JAWS_Asynch_IO::~JAWS_Asynch_IO (void) +{ + ACE_OS::closesocket (this->handle_); +} + +void +JAWS_Asynch_IO::read (ACE_Message_Block& mb, + int size) +{ + ACE_Asynch_Read_Stream ar; + + if (ar.open (*this, this->handle_) == -1 + || ar.read (mb, size) == -1) + this->handler_->read_error (); +} + +// This method will be called when an asynchronous read completes on a +// stream. + +void +JAWS_Asynch_IO::handle_read_stream (const ACE_Asynch_Read_Stream::Result &result) +{ + // This callback is for this->receive_file() + if (result.act () != 0) + { + int code = 0; + if (result.success () && result.bytes_transferred () != 0) + { + if (result.message_block ().length () == result.message_block ().size ()) + code = ACE_Filecache_Handle::ACE_SUCCESS; + else + { + ACE_Asynch_Read_Stream ar; + if (ar.open (*this, this->handle_) == -1 + || ar.read (result.message_block (), + result.message_block ().size () - result.message_block ().length (), + result.act ()) == -1) + code = -1; + else + return; + } + } + else + code = -1; + + if (code == ACE_Filecache_Handle::ACE_SUCCESS) + this->handler_->receive_file_complete (); + else + this->handler_->receive_file_error (code); + + delete &result.message_block (); + delete (ACE_Filecache_Handle *) result.act (); + } + else + { + // This callback is for this->read() + if (result.success () + && result.bytes_transferred () != 0) + this->handler_->read_complete (result.message_block ()); + else + this->handler_->read_error (); + } +} + +void +JAWS_Asynch_IO::receive_file (const char *filename, + void *initial_data, + int initial_data_length, + int entire_length) +{ + ACE_Message_Block *mb = 0; + ACE_Filecache_Handle *handle; + + ACE_NEW (handle, ACE_Filecache_Handle (filename, entire_length, ACE_NOMAP)); + + int result = handle->error (); + + if (result == ACE_Filecache_Handle::ACE_SUCCESS) + { + ACE_OS::memcpy (handle->address (), + initial_data, + initial_data_length); + + int bytes_to_read = entire_length - initial_data_length; + + ACE_NEW (mb, ACE_Message_Block ((char *)handle->address () + + initial_data_length, bytes_to_read)); + + if (mb == 0) + { + errno = ENOMEM; + result = -1; + } + else + { + ACE_Asynch_Read_Stream ar; + + if (ar.open (*this, this->handle_) == -1 + || ar.read (*mb, mb->size () - mb->length (), handle) == -1) + result = -1; + } + } + + if (result != ACE_Filecache_Handle::ACE_SUCCESS) + { + this->handler_->receive_file_error (result); + delete mb; + delete handle; + } +} + +void +JAWS_Asynch_IO::transmit_file (const char *filename, + const char *header, + int header_size, + const char *trailer, + int trailer_size) +{ + ACE_Asynch_Transmit_File::Header_And_Trailer *header_and_trailer = 0; + ACE_Filecache_Handle *handle = new ACE_Filecache_Handle (filename, ACE_NOMAP); + + int result = handle->error (); + + if (result == ACE_Filecache_Handle::ACE_SUCCESS) + { + ACE_Message_Block header_mb (header, header_size); + ACE_Message_Block trailer_mb (trailer, trailer_size); + + header_and_trailer = new ACE_Asynch_Transmit_File::Header_And_Trailer + (&header_mb, header_size, &trailer_mb, trailer_size); + + ACE_Asynch_Transmit_File tf; + + if (tf.open (*this, this->handle_) == -1 + || tf.transmit_file (handle->handle (), // file handle + header_and_trailer, // header and trailer data + 0, // bytes_to_write + 0, // offset + 0, // offset_high + 0, // bytes_per_send + 0, // flags + handle // act + ) == -1) + result = -1; + } + + if (result != ACE_Filecache_Handle::ACE_SUCCESS) + { + this->handler_->transmit_file_error (result); + delete header_and_trailer; + delete handle; + } +} + + +// This method will be called when an asynchronous transmit file completes. +void +JAWS_Asynch_IO::handle_transmit_file (const ACE_Asynch_Transmit_File::Result &result) +{ + if (result.success ()) + this->handler_->transmit_file_complete (); + else + this->handler_->transmit_file_error (-1); + + delete result.header_and_trailer (); + delete (ACE_Filecache_Handle *) result.act (); +} + +void +JAWS_Asynch_IO::send_confirmation_message (const char *buffer, + int length) +{ + this->send_message (buffer, length, CONFORMATION); +} + +void +JAWS_Asynch_IO::send_error_message (const char *buffer, + int length) +{ + this->send_message (buffer, length, ERROR_MESSAGE); +} + +void +JAWS_Asynch_IO::send_message (const char *buffer, + int length, + int act) +{ + ACE_Message_Block *mb; + ACE_NEW (mb, ACE_Message_Block (buffer, length)); + + if (mb == 0) + { + this->handler_->error_message_complete (); + return; + } + + ACE_Asynch_Write_Stream aw; + if (aw.open (*this, this->handle_) == -1 + || aw.write (*mb, length, (void *) act) == -1) + { + mb->release (); + + if (act == CONFORMATION) + this->handler_->confirmation_message_complete (); + else + this->handler_->error_message_complete (); + } +} + +void +JAWS_Asynch_IO::handle_write_stream (const ACE_Asynch_Write_Stream::Result &result) +{ + result.message_block ().release (); + + if (result.act () == (void *) CONFORMATION) + this->handler_->confirmation_message_complete (); + else + this->handler_->error_message_complete (); +} + +#endif /* ACE_WIN32 */ + +//-------------------Adding SYNCH IO no Caching + +JAWS_Synch_IO_No_Cache::JAWS_Synch_IO_No_Cache (void) + : handle_ (ACE_INVALID_HANDLE) +{ +} + +JAWS_Synch_IO_No_Cache::~JAWS_Synch_IO_No_Cache (void) +{ + ACE_OS::closesocket (this->handle_); +} + +ACE_HANDLE +JAWS_Synch_IO_No_Cache::handle (void) const +{ + return this->handle_; +} + +void +JAWS_Synch_IO_No_Cache::handle (ACE_HANDLE handle) +{ + this->handle_ = handle; +} + +void +JAWS_Synch_IO_No_Cache::read (ACE_Message_Block &mb, + int size) +{ + ACE_SOCK_Stream stream; + stream.set_handle (this->handle_); + int result = stream.recv (mb.wr_ptr (), size); + + if (result <= 0) + this->handler_->read_error (); + else + { + mb.wr_ptr (result); + this->handler_->read_complete (mb); + } +} + +void +JAWS_Synch_IO_No_Cache::receive_file (const char *filename, + void *initial_data, + int initial_data_length, + int entire_length) +{ + //ugly hack to send HTTP_Status_Code::STATUS_FORBIDDEN + this->handler_->receive_file_error (5); + + //To get rid of warnings on some platforms + //NOTE: this function is necessary because the base class + //version of the function is pure virtual + ACE_UNUSED_ARG (filename); + ACE_UNUSED_ARG (initial_data); + ACE_UNUSED_ARG (initial_data_length); + ACE_UNUSED_ARG (entire_length); +} + +void +JAWS_Synch_IO_No_Cache::transmit_file (const char *filename, + const char *header, + int header_size, + const char *trailer, + int trailer_size) +{ + int result = 0; + + // Can we access the file? + if (ACE_OS::access (filename, R_OK) == -1) + { + //ugly hack to send in HTTP_Status_Code::STATUS_NOT_FOUND + result = ACE_Filecache_Handle::ACE_ACCESS_FAILED; + this->handler_->transmit_file_error (result); + return; + } + + ACE_stat stat; + + // Can we stat the file? + if (ACE_OS::stat (filename, &stat) == -1) + { + //ugly hack to send HTTP_Status_Code::STATUS_FORBIDDEN + result = ACE_Filecache_Handle::ACE_STAT_FAILED; + this->handler_->transmit_file_error (result); + return; + } + + ssize_t size = stat.st_size; + + // Can we open the file? + ACE_HANDLE handle = ACE_OS::open (filename, O_RDONLY); + if (handle == ACE_INVALID_HANDLE) + { + //ugly hack to send HTTP_Status_Code::STATUS_FORBIDDEN + result = ACE_Filecache_Handle::ACE_OPEN_FAILED; + this->handler_->transmit_file_error (result); + return; + } + + char* f = new char[size]; + auto_ptr<char> file (f); + + ACE_OS::read_n (handle, f, size); + + ACE_SOCK_Stream stream; + stream.set_handle (this->handle_); + + if ((stream.send_n (header, header_size) == header_size) + && (stream.send_n (f, size) == size) + && (stream.send_n (trailer, trailer_size) == trailer_size)) + this->handler_->transmit_file_complete (); + else + { + //ugly hack to default to HTTP_Status_Code::STATUS_INTERNAL_SERVER_ERROR + result = -1; + this->handler_->transmit_file_error (result); + return; + } +} + +void +JAWS_Synch_IO_No_Cache::send_confirmation_message (const char *buffer, + int length) +{ + this->send_message (buffer, length); + this->handler_->confirmation_message_complete (); +} + +void +JAWS_Synch_IO_No_Cache::send_error_message (const char *buffer, + int length) +{ + this->send_message (buffer, length); + this->handler_->error_message_complete (); +} + +void +JAWS_Synch_IO_No_Cache::send_message (const char *buffer, + int length) +{ + ACE_SOCK_Stream stream; + stream.set_handle (this->handle_); + stream.send_n (buffer, length); +} + diff --git a/ACE/apps/JAWS/server/IO.h b/ACE/apps/JAWS/server/IO.h new file mode 100644 index 00000000000..fd5ae0d64b2 --- /dev/null +++ b/ACE/apps/JAWS/server/IO.h @@ -0,0 +1,296 @@ +/* -*- c++ -*- */ +// Hey, Emacs! This is a C++ file! +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// jaws +// +// = FILENAME +// IO.h +// +// = AUTHOR +// James Hu +// +// ============================================================================ + +#ifndef JAWS_IO_H +#define JAWS_IO_H + +#include "ace/ACE.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Asynch_IO.h" + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL +class ACE_Message_Block; +ACE_END_VERSIONED_NAMESPACE_DECL + +class JAWS_IO_Handler; + + +class JAWS_IO + // = TITLE + // + // This class defines the abstract interface for an I/O class in + // the context of Web-likes servers + // + // = DESCRIPTION + // + // An I/O class should have the following interface. Derived + // classes will define the exactly how the I/O will take place + // (Asynchronous, Synchronous, Reactive) +{ +public: + JAWS_IO (void); + virtual ~JAWS_IO (void); + void handler (JAWS_IO_Handler *handler); + + virtual void handle (ACE_HANDLE h) = 0; + virtual ACE_HANDLE handle (void) const = 0; + + // James, please add documentation here. + + virtual void read (ACE_Message_Block& mb, int size) = 0; + // read from the handle size bytes into the message block. + + virtual void transmit_file (const char *filename, + const char *header, + int header_size, + const char *trailer, + int trailer_size) = 0; + // send header, filename, trailer to the handle. + + virtual void receive_file (const char *filename, + void *initial_data, + int initial_data_length, + int entire_length) = 0; + // read data from the handle and store in filename. + + virtual void send_confirmation_message (const char *buffer, int length) = 0; + // send a confirmation message to the handle. + + virtual void send_error_message (const char *buffer, int length) = 0; + // send an error message to the handle. + +protected: + JAWS_IO_Handler *handler_; +}; + +class JAWS_IO_Handler + // = TITLE + // + // This class defines the abstract interface for an I/O handler class in + // the context of Web-likes servers + // + // = DESCRIPTION +{ +public: + + /// Destructor. + virtual ~JAWS_IO_Handler (void); + + virtual void read_complete (ACE_Message_Block &data) = 0; + // This method is called by the IO class when new client data shows + // up. + + virtual void read_error (void) = 0; + // This method is called by the IO class when there was an error in + // reading new data from the client. + + virtual void transmit_file_complete (void) = 0; + // This method is called by the IO class when the requested file has + // been successfully transmitted to the client. + + virtual void transmit_file_error (int result) = 0; + // This method is called by the IO class when there was an error in + // transmitting the requested file to the client. + + virtual void receive_file_complete (void) = 0; + // This method is called by the IO class when the requested file has + // been successfully received from the client. + + virtual void receive_file_error (int result) = 0; + // This method is called by the IO class when there was an error in + // receiving the requested file from the client. + + virtual void write_error (void) = 0; + // This method is called by the IO class when there was an error in + // writing data to the client. + + virtual void confirmation_message_complete (void) = 0; + // This method is called by the IO class when the confirmation + // message has been delivered to the client. + + virtual void error_message_complete (void) = 0; + // This method is called by the IO class when the error message has + // been delivered to the client. + +}; + +class JAWS_Synch_IO : public JAWS_IO + // = TITLE + // + // This class defines the interface for a Synchronous I/O class. + // + // = DESCRIPTION +{ +public: + JAWS_Synch_IO (void); + + ~JAWS_Synch_IO (void); + + virtual void handle (ACE_HANDLE h); + virtual ACE_HANDLE handle (void) const; + + void read (ACE_Message_Block& mb, int size); + + void transmit_file (const char *filename, + const char *header, + int header_size, + const char *trailer, + int trailer_size); + + void receive_file (const char *filename, + void *initial_data, + int initial_data_length, + int entire_length); + + void send_confirmation_message (const char *buffer, + int length); + + void send_error_message (const char *buffer, + int length); + +protected: + virtual void send_message (const char *buffer, + int length); + + ACE_HANDLE handle_; +}; + +// This only works on Win32 +#if defined (ACE_WIN32) + +class JAWS_Asynch_IO : public JAWS_IO, public ACE_Handler + // = TITLE + // + // This class defines the interface for a Asynchronous I/O class. + // + // = DESCRIPTION +{ +public: + JAWS_Asynch_IO (void); + + ~JAWS_Asynch_IO (void); + + virtual void handle (ACE_HANDLE h) { ACE_Handler::handle (h); }; + virtual ACE_HANDLE handle (void) const { return ACE_Handler::handle (); }; + + void read (ACE_Message_Block& mb, int size); + + void transmit_file (const char *filename, + const char *header, + int header_size, + const char *trailer, + int trailer_size); + + void receive_file (const char *filename, + void *initial_data, + int initial_data_length, + int entire_length); + + void send_confirmation_message (const char *buffer, + int length); + + void send_error_message (const char *buffer, + int length); + +protected: + enum Message_Types + { + CONFORMATION, + ERROR_MESSAGE + }; + + virtual void send_message (const char *buffer, + int length, + int act); + + virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result &result); + // This method will be called when an asynchronous read completes on + // a stream. + + virtual void handle_write_stream (const ACE_Asynch_Write_Stream::Result &result); + // This method will be called when an asynchronous write completes + // on a stream. + + virtual void handle_transmit_file (const ACE_Asynch_Transmit_File::Result &result); + // This method will be called when an asynchronous transmit file + // completes. +}; + +#endif /* ACE_WIN32 */ + + +//-------------------Adding SYNCH IO no Caching + +class JAWS_Synch_IO_No_Cache : public JAWS_IO + // = TITLE + // + // This class defines the interface for a Synchronous I/O class, + // however in this class we do not use any caching. + // + // = DESCRIPTION + // + // Wondering how this is useful? + // The ACE_Filecache ACE_NOMAP option is broken and even if it were not, there + // are other use cases in which we want to avoid caching altogether. For example, + // we use JAWS in conjunction with the CIAO Repository Manager, however the two + // do not have any explicit knowledge of each other. Therefore if the RM tried + // to remove a package and its files from disk, its operation would [partially] + // fail if JAWS still holds some of the files in its cache. + // +{ +public: + JAWS_Synch_IO_No_Cache (void); + + ~JAWS_Synch_IO_No_Cache (void); + + virtual void handle (ACE_HANDLE h); + virtual ACE_HANDLE handle (void) const; + + void read (ACE_Message_Block& mb, int size); + + void transmit_file (const char *filename, + const char *header, + int header_size, + const char *trailer, + int trailer_size); + + void receive_file (const char *filename, + void *initial_data, + int initial_data_length, + int entire_length); + + void send_confirmation_message (const char *buffer, + int length); + + void send_error_message (const char *buffer, + int length); + +protected: + virtual void send_message (const char *buffer, + int length); + + ACE_HANDLE handle_; +}; + +//------------------- + +#endif /* JAWS_IO_H */ + diff --git a/ACE/apps/JAWS/server/JAWS_Concurrency.cpp b/ACE/apps/JAWS/server/JAWS_Concurrency.cpp new file mode 100644 index 00000000000..4d7388d6f8a --- /dev/null +++ b/ACE/apps/JAWS/server/JAWS_Concurrency.cpp @@ -0,0 +1,82 @@ +// $Id$ + +#include "JAWS_Concurrency.h" + +ACE_RCSID(server, JAWS_Concurrency, "$Id$") + +JAWS_Concurrency_Base::JAWS_Concurrency_Base (void) +{ +} + +int +JAWS_Concurrency_Base::put (ACE_Message_Block *mb, ACE_Time_Value *tv) +{ + return this->putq (mb, tv); +} + +int +JAWS_Concurrency_Base::svc (void) +{ + int result = 0; + + for (;;) + { + ACE_Message_Block *mb; + + // At this point we could set a timeout value so that the + // threading strategy can delete a thread if there is nothing to + // do. Carefully think how to implement it so you don't leave + // yourself with 0 threads. + + result = this->getq (mb); + if (result == -1 || mb == 0) + break; + + this->put_next (mb); + } + return 0; +} + +JAWS_Dispatch_Policy::JAWS_Dispatch_Policy (void) +{ +} + +JAWS_Dispatch_Policy::~JAWS_Dispatch_Policy (void) +{ +} + +JAWS_Dispatcher::JAWS_Dispatcher (JAWS_Dispatch_Policy *policy) + : policy_(policy) +{ +} + +JAWS_Thread_Pool_Task::JAWS_Thread_Pool_Task (long flags, + int nthreads, + int maxthreads) + : nthreads_ (nthreads), + maxthreads_ (maxthreads) +{ + if (this->activate (flags, nthreads) == -1) + ACE_ERROR ((LM_ERROR, "%p\n", "JAWS_Thread_Pool_Task::activate")); +} + +JAWS_Thread_Per_Task::JAWS_Thread_Per_Task (long flags, int maxthreads) + : flags_ (flags), + maxthreads_ (maxthreads) +{ +} + +int +JAWS_Thread_Per_Task::put (ACE_Message_Block *mb, ACE_Time_Value *tv) +{ + const int force_active = 1; + const int nthreads = 1; + + if (this->activate (this->flags_, nthreads, force_active) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "JAWS_Thread_Pool_Task::activate"), + -1); + + this->putq (mb, tv); + + return 0; +} diff --git a/ACE/apps/JAWS/server/JAWS_Concurrency.h b/ACE/apps/JAWS/server/JAWS_Concurrency.h new file mode 100644 index 00000000000..7096d1dbefa --- /dev/null +++ b/ACE/apps/JAWS/server/JAWS_Concurrency.h @@ -0,0 +1,101 @@ +/* -*- c++ -*- */ +// $Id$ + +#ifndef JAWS_CONCURRENCY_H +#define JAWS_CONCURRENCY_H + +#include "ace/config-all.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Task.h" +#include "IO.h" + +class JAWS_Concurrency_Base : public ACE_Task<ACE_SYNCH> + // = TITLE + // Base class for different concurrency models + // + // = DESCRIPTION + // Provides a default implementaion of the virtual put() method + // which calls putq(), but can be overloaded to do something + // synchronously, such as call put_next(). + +{ +public: + JAWS_Concurrency_Base (void); + virtual int put (ACE_Message_Block *mb, ACE_Time_Value *tv = 0); + virtual int svc (void); +}; + +class JAWS_Dispatch_Policy + // = TITLE + // Policy mechanism for choosing different concurrency models. + // + // = DESCRIPTION + // Given some (unspecified) state, decides what the concurrency + // model should be. (For now, we always return the same model.) +{ +public: + JAWS_Dispatch_Policy (void); + virtual ~JAWS_Dispatch_Policy (void); + virtual JAWS_Concurrency_Base * update (void *state = 0) = 0; +}; + +class JAWS_Dispatcher + // = TITLE + // The class that is responsible to delivering events to the + // appropriate concurrency mechanism. + // + // = DESCRIPTION + // JAWS_IO_Handler calls into the dispatcher so that the completed + // IO can find a thread to take care of it. +{ +public: + JAWS_Dispatcher (JAWS_Dispatch_Policy *policy); + + int dispatch (JAWS_IO_Handler *ioh); + +private: + JAWS_Dispatch_Policy *policy_; +}; + +class JAWS_Thread_Pool_Task : public JAWS_Concurrency_Base + // = TITLE + // Used to implement Thread Pool Concurrency Strategy + // + // = DESCRIPTION + // This task is created to hold a pool of threads that receive + // requests through the message queue. +{ +public: + JAWS_Thread_Pool_Task (long flags = THR_NEW_LWP, + int nthreads = 5, + int maxthreads = 20); + +private: + int nthreads_; + int maxthreads_; +}; + +class JAWS_Thread_Per_Task : public JAWS_Concurrency_Base + // = TITLE + // Used to implement Thread Per Request Concurrency Strategy + // + // = DESCRIPTION + // As each new message arrives from the queue, a new thread is + // spawned to handle it. This is done by overloading put to call + // activate. +{ +public: + JAWS_Thread_Per_Task (long flags = THR_NEW_LWP, int maxthreads = 20); + + virtual int put (ACE_Message_Block *mb, ACE_Time_Value *tv = 0); + +private: + long flags_; + int maxthreads_; +}; + +#endif /* !defined (JAWS_CONCURRENCY_H) */ diff --git a/ACE/apps/JAWS/server/JAWS_Pipeline.cpp b/ACE/apps/JAWS/server/JAWS_Pipeline.cpp new file mode 100644 index 00000000000..f2ea3ecfbe9 --- /dev/null +++ b/ACE/apps/JAWS/server/JAWS_Pipeline.cpp @@ -0,0 +1,29 @@ +// $Id$ + +#include "JAWS_Pipeline.h" + +ACE_RCSID(server, JAWS_Pipeline, "$Id$") + +JAWS_Pipeline::JAWS_Pipeline (void) +{ +} + +int +JAWS_Pipeline::open (void *) +{ + // Simply call into the virtual svc() method. + if (this->svc () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "JAWS_Pipeline::svc"), + -1); + return 0; +} + +int +JAWS_Pipeline::close (u_long) +{ + return 0; +} + +#include "JAWS_Pipeline_Handler.cpp" diff --git a/ACE/apps/JAWS/server/JAWS_Pipeline.h b/ACE/apps/JAWS/server/JAWS_Pipeline.h new file mode 100644 index 00000000000..919c982977c --- /dev/null +++ b/ACE/apps/JAWS/server/JAWS_Pipeline.h @@ -0,0 +1,36 @@ +/* -*- c++ -*- */ +// $Id$ + +#ifndef JAWS_PIPELINE_H +#define JAWS_PIPELINE_H + +#include "ace/config-all.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Service_Config.h" +#include "ace/Stream.h" +#include "ace/Module.h" +#include "ace/Task.h" + +typedef ACE_Stream<ACE_NULL_SYNCH> JAWS_Pipeline_Stream; +typedef ACE_Module<ACE_NULL_SYNCH> JAWS_Pipeline_Module; +typedef ACE_Task<ACE_NULL_SYNCH> JAWS_Pipeline_Task; + +class JAWS_Pipeline : public JAWS_Pipeline_Task + // = TITLE + // Methods that are common to pipeline components +{ +public: + JAWS_Pipeline (void); + // ACE_Task hooks + + virtual int open (void * = 0); + virtual int close (u_long = 0); +}; + +#include "JAWS_Pipeline_Handler.h" + +#endif /* !defined (JAWS_PIPELINE_H) */ diff --git a/ACE/apps/JAWS/server/JAWS_Pipeline_Handler.cpp b/ACE/apps/JAWS/server/JAWS_Pipeline_Handler.cpp new file mode 100644 index 00000000000..e67a99545bb --- /dev/null +++ b/ACE/apps/JAWS/server/JAWS_Pipeline_Handler.cpp @@ -0,0 +1,25 @@ +// $Id$ + +#ifndef JAWS_PIPELINE_HANDLER_CPP +#define JAWS_PIPELINE_HANDLER_CPP + +#include "JAWS_Pipeline_Handler.h" + +ACE_RCSID(server, JAWS_Pipeline_Handler, "$Id$") + +template <class TYPE> +JAWS_Pipeline_Handler<TYPE>::JAWS_Pipeline_Handler (void) +{ +} + +template <class TYPE> int +JAWS_Pipeline_Handler<TYPE>::put (ACE_Message_Block *mb, ACE_Time_Value *tv) +{ + TYPE *data = dynamic_cast<TYPE *> (mb->data_block ()); + + int status = this->handle_input (data, tv); + + return (status != -1) ? this->put_next (mb, tv) : -1; +} + +#endif /* !defined (JAWS_PIPELINE_HANDLER_CPP) */ diff --git a/ACE/apps/JAWS/server/JAWS_Pipeline_Handler.h b/ACE/apps/JAWS/server/JAWS_Pipeline_Handler.h new file mode 100644 index 00000000000..7e9becb6f5e --- /dev/null +++ b/ACE/apps/JAWS/server/JAWS_Pipeline_Handler.h @@ -0,0 +1,29 @@ +/* -*- c++ -*- */ +// $Id$ + +#ifndef JAWS_PIPELINE_HANDLER_H +#define JAWS_PIPELINE_HANDLER_H + +#include "JAWS_Pipeline.h" + +template <class TYPE> +class JAWS_Pipeline_Handler : public JAWS_Pipeline_Task + // = TITLE + // Methods that are common to pipeline components +{ +public: + JAWS_Pipeline_Handler (void); + // ACE_Task hooks + + virtual int put (ACE_Message_Block *mb, ACE_Time_Value *tv = 0); + // inherited from ACE_Task + + virtual int handle_put (TYPE *data, ACE_Time_Value *tv) = 0; + // Callback hook for specialized data processing +}; + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) +#include "JAWS_Pipeline_Handler.cpp" +#endif + +#endif /* !defined (JAWS_PIPELINE_HANDLER_H) */ diff --git a/ACE/apps/JAWS/server/Makefile.am b/ACE/apps/JAWS/server/Makefile.am new file mode 100644 index 00000000000..d13d91ad381 --- /dev/null +++ b/ACE/apps/JAWS/server/Makefile.am @@ -0,0 +1,95 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## /acebuilds/ACE_wrappers-repository/bin/mwc.pl -include /acebuilds/MPC/config -include /acebuilds/MPC/templates -feature_file /acebuilds/ACE_wrappers-repository/local.features -noreldefs -type automake -exclude build,Kokyu + +ACE_BUILDDIR = $(top_builddir) +ACE_ROOT = $(top_srcdir) + + +## Makefile.JAWS.am + +if BUILD_ACE_FILECACHE +if !BUILD_ACE_FOR_TAO +if !BUILD_USES_WCHAR + +noinst_LTLIBRARIES = libJAWS.la + +libJAWS_la_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + -DACE_BUILD_SVC_DLL + +libJAWS_la_SOURCES = \ + HTTP_Config.cpp \ + HTTP_Handler.cpp \ + HTTP_Helpers.cpp \ + HTTP_Request.cpp \ + HTTP_Response.cpp \ + HTTP_Server.cpp \ + IO.cpp \ + JAWS_Concurrency.cpp \ + JAWS_Pipeline.cpp \ + Parse_Headers.cpp + +noinst_HEADERS = \ + HTTP_Config.h \ + HTTP_Handler.h \ + HTTP_Helpers.h \ + HTTP_Request.h \ + HTTP_Response.h \ + HTTP_Server.h \ + IO.h \ + JAWS_Concurrency.h \ + JAWS_Pipeline.h \ + Parse_Headers.h + +endif !BUILD_USES_WCHAR +endif !BUILD_ACE_FOR_TAO +endif BUILD_ACE_FILECACHE + +## Makefile.JAWS_server.am + +if BUILD_ACE_FILECACHE +if !BUILD_ACE_FOR_TAO +if !BUILD_USES_WCHAR +noinst_PROGRAMS = main + +main_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +main_SOURCES = \ + main.cpp \ + HTTP_Config.h \ + HTTP_Handler.h \ + HTTP_Helpers.h \ + HTTP_Request.h \ + HTTP_Response.h \ + HTTP_Server.h \ + IO.h \ + JAWS_Concurrency.h \ + JAWS_Pipeline.h \ + JAWS_Pipeline_Handler.h \ + Parse_Headers.h + +main_LDADD = \ + libJAWS.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_USES_WCHAR +endif !BUILD_ACE_FOR_TAO +endif BUILD_ACE_FILECACHE + +## Clean up template repositories, etc. +clean-local: + -rm -f *~ *.bak *.rpo *.sym lib*.*_pure_* core core.* + -rm -f gcctemp.c gcctemp so_locations *.ics + -rm -rf cxx_repository ptrepository ti_files + -rm -rf templateregistry ir.out + -rm -rf ptrepository SunWS_cache Templates.DB diff --git a/ACE/apps/JAWS/server/Parse_Headers.cpp b/ACE/apps/JAWS/server/Parse_Headers.cpp new file mode 100644 index 00000000000..0d7dd273c59 --- /dev/null +++ b/ACE/apps/JAWS/server/Parse_Headers.cpp @@ -0,0 +1,362 @@ +// $Id$ + +#include "ace/Log_Msg.h" + +#include "Parse_Headers.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_strings.h" +#include "ace/OS_NS_stdlib.h" +#include "ace/os_include/os_ctype.h" + +ACE_RCSID(server, Parse_Headers, "$Id$") + +// Implementation of class Headers + +Headers::Headers (void) : done_(0) +{ +} + +Headers::~Headers (void) +{ +} + +void +Headers::recognize (const char * const header) +{ + (void)this->map_[header]; +} + +void +Headers::parse_header_line (char * const header_line) +{ + char *ptr = header_line; + char *buf = header_line; + int offset = 1; + + ptr = ACE_OS::strchr (header_line, '\n'); + + if (ptr > header_line && ptr[-1] == '\r') + { + ptr--; + offset++; + } + + if (ptr == header_line) + { + this->done_ = 1; + return; + } + + *ptr = '\0'; + ptr += offset; + + char *value = 0; + char *header = ACE_OS::strtok_r (buf, ":", &value); + + ACE_DEBUG((LM_DEBUG, " (%t) Headers::parse_header_line [%s]\n", + header ? header : "<empty>")); + + if (header != NULL && this->map_.mapped (header)) + { + while (isspace (*value)) + value++; + + this->map_[header] = value; + + ACE_DEBUG((LM_DEBUG, " (%t) Headers::parse_header_line <%s>\n", + value ? value : "<empty>")); + } + + // Write back the unused portion of the input. + ACE_OS::memmove (header_line, ptr, ACE_OS::strlen(ptr) + 1); +} + +int +Headers::complete_header_line (char *const header_line) +{ + // Algorithm -- + // Scan for end of line marker. + // If the next character is linear white space, then unfold the header. + // Else, if the next character is printable, we have a complete header line. + // Else, presumably the next character is '\0', so the header is incomplete. + + // return -1 if end of line but not complete header line + // return 0 if no end of line marker + // return 1 if complete header line + + char *ptr = header_line; + int offset; + + if (!this->end_of_line (ptr, offset)) + return 0; + + if (ptr == header_line) + { + ACE_OS::memmove (ptr, ptr+offset, ACE_OS::strlen (ptr + offset) + 1); + this->done_ = 1; + ACE_DEBUG ((LM_DEBUG, " (%t) no more headers\n")); + return 0; + } + + do + { + switch (ptr[offset]) + { + case ' ': + case '\t': + ACE_OS::memmove (ptr, ptr+offset, ACE_OS::strlen (ptr + offset) + 1); + break; + + case '\n': + case '\r': + return 1; + + default: + if (isalpha (ptr[offset])) + return 1; + else + return -1; + } + } + while (this->end_of_line (ptr, offset) != 0); + + return 0; +} + +int +Headers::end_of_headers (void) const +{ + return this->done_; +} + +Headers_Map_Item & +Headers::operator[] (const char * const header) +{ + return this->map_[header]; +} + +const Headers_Map_Item & +Headers::operator[] (const char * const header) const +{ + return this->map_[header]; +} + +int +Headers::end_of_line (char *&line, int &offset) const +{ + char *old_line = line; + char *ptr = ACE_OS::strchr (old_line, '\n'); + + if (ptr == NULL) + return 0; + + line = ptr; + offset = 1; + + if (line > old_line + && line[-1] == '\r') + { + line--; + offset = 2; + } + + return 1; +} + + +// Implementation of class Headers_Map + +Headers_Map::Headers_Map (void) + : num_headers_(0) +{ +} + +Headers_Map::~Headers_Map (void) +{ +} + +Headers_Map_Item::Headers_Map_Item (void) + : header_(0), + value_(0) +{ +} + +Headers_Map_Item::~Headers_Map_Item (void) +{ + ACE_OS::free ((void *) this->header_); + ACE_OS::free ((void *) this->value_); + this->header_ = this->value_ = 0; +} + +// Headers_Map_Item::operator const char * (void) const +// { +// return this->value_ == NULL ? this->no_value_ : this->value_; +// } + +Headers_Map_Item & +Headers_Map_Item::operator= (char * value) +{ + ACE_OS::free ((void *) this->value_); + this->value_ = ACE_OS::strdup (value); + return *this; +} + +Headers_Map_Item & +Headers_Map_Item::operator= (const char * value) +{ + ACE_OS::free ((void *) this->value_); + this->value_ = ACE_OS::strdup (value); + return *this; +} + +Headers_Map_Item & +Headers_Map_Item::operator= (const Headers_Map_Item & mi) +{ + ACE_OS::free ((void *) this->value_); + ACE_OS::free ((void *) this->header_); + this->header_ = ACE_OS::strdup (mi.header_); + this->value_ = (mi.value_ ? ACE_OS::strdup (mi.value_) : 0); + return *this; +} + +const char * +Headers_Map_Item::header (void) const +{ + return this->header_; +} + +const char * +Headers_Map_Item::value (void) const +{ + return this->value_; +} + +Headers_Map_Item & +Headers_Map::operator[] (const char * const header) +{ + Headers_Map_Item *item_ptr; + + item_ptr = this->find (header); + + if (item_ptr == NULL) + item_ptr = this->place (header); + + return *item_ptr; +} + +const Headers_Map_Item & +Headers_Map::operator[] (const char * const header) const +{ + Headers_Map_Item *item_ptr; + Headers_Map *mutable_this = (Headers_Map *)this; + + item_ptr = this->find (header); + + if (item_ptr == NULL) + item_ptr = mutable_this->place (header); + + return *item_ptr; +} + +int +Headers_Map::mapped (const char * const header) const +{ + int result = this->find (header) != NULL; + + return result; +} + +Headers_Map_Item * +Headers_Map::find (const char * const header) const +{ + Headers_Map *const mutable_this = (Headers_Map *) this; + + mutable_this->garbage_.header_ = header; +#if 0 + Headers_Map_Item *mi_ptr = (Headers_Map_Item *) + ::bsearch (&this->garbage_, + this->map_, + this->num_headers_, + sizeof (Headers_Map_Item), + Headers_Map::compare); +#else + int i = 0; + int j = this->num_headers_; + + while (i < j-1) + { + int k = (i+j)/2; + if (Headers_Map::compare (&this->garbage_, this->map_+k) < 0) + j = k; + else + i = k; + } + + Headers_Map_Item *mi_ptr = mutable_this->map_ + i; + if (Headers_Map::compare (&this->garbage_, mi_ptr) != 0) + mi_ptr = 0; +#endif + + mutable_this->garbage_.header_ = 0; + + return mi_ptr; +} + +Headers_Map_Item * +Headers_Map::place (const char *const header) +{ + this->garbage_.header_ = ACE_OS::strdup (header); + + int i = this->num_headers_++; + ACE_OS::free ((void *) this->map_[i].header_); + ACE_OS::free ((void *) this->map_[i].value_); + this->map_[i].header_ = 0; + this->map_[i].value_ = 0; + Headers_Map_Item temp_item; + + while (i > 0) + { + if (Headers_Map::compare (&this->garbage_, + &this->map_[i - 1]) > 0) + break; + + this->map_[i].header_ = this->map_[i - 1].header_; + this->map_[i].value_ = this->map_[i - 1].value_; + this->map_[i - 1].header_ = 0; + this->map_[i - 1].value_ = 0; + + i--; + } + + this->map_[i].header_ = this->garbage_.header_; + this->map_[i].value_ = this->garbage_.value_; + + this->garbage_.header_ = 0; + + return &this->map_[i]; +} + +int +Headers_Map::compare (const void *item1, + const void *item2) +{ + Headers_Map_Item *a, *b; + int result; + + a = (Headers_Map_Item *) item1; + b = (Headers_Map_Item *) item2; + + if (a->header_ == 0 || b->header_ == 0) + { + if (a->header_ == 0 && b->header_ == 0) + result = 0; + else if (a->header_ == 0) + result = 1; + else + result = -1; + } + else + result = ACE_OS::strcasecmp (a->header_, b->header_); + + return (result < 0) ? -1 : (result > 0); +} diff --git a/ACE/apps/JAWS/server/Parse_Headers.h b/ACE/apps/JAWS/server/Parse_Headers.h new file mode 100644 index 00000000000..f5d564d8d0b --- /dev/null +++ b/ACE/apps/JAWS/server/Parse_Headers.h @@ -0,0 +1,121 @@ +/* -*- c++ -*- */ +// Hey, Emacs! This is a C++ file! +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// jaws +// +// = FILENAME +// Parse_Headers.h +// +// = AUTHOR +// James Hu +// +// ============================================================================ + +#ifndef PARSE_HEADERS_H +#define PARSE_HEADERS_H + +#include "ace/config-all.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +class Headers_Map_Item +{ +friend class Headers_Map; +friend class Headers; + +private: + Headers_Map_Item (void); + ~Headers_Map_Item (void); + + // operator const char * (void) const; + Headers_Map_Item &operator= (char *); + Headers_Map_Item &operator= (const char *); + Headers_Map_Item &operator= (const Headers_Map_Item &); + +public: + const char *header (void) const; + const char *value (void) const; + +private: + const char *header_; + const char *value_; +}; + +class Headers_Map + // = TITLE + // Map textual headings to header values (e.g. "Subject:" maps to + // "Re: My left foot" +{ +public: + Headers_Map (void); + ~Headers_Map (void); + + Headers_Map_Item &operator[] (const char *const header); + const Headers_Map_Item &operator[] (const char *const header) const; + + enum + { + MAX_HEADERS = 100 + }; + + int mapped (const char *const header) const; + +private: + Headers_Map_Item *find (const char *const header) const; + Headers_Map_Item *place (const char *const header); + static int compare (const void *item1, const void *item2); + +private: + Headers_Map_Item map_[MAX_HEADERS]; + Headers_Map_Item garbage_; + + int num_headers_; +}; + +class Headers + // = TITLE + // A general mechanism to parse headers of Internet text headers. + // + // = DESCRIPTION + // Allow interesting headers to be inserted and later associated + // with values. This implementation assumes the parsing of headers + // will be done from ACE_Message_Blocks. +{ +public: + Headers (void); + ~Headers (void); + + void recognize (const char *const header); + + void parse_header_line (char *const header_line); + + int complete_header_line (char *const header_line); + // -1 -> end of line but not complete header line + // 0 -> no end of line marker + // 1 -> complete header line + + int end_of_headers (void) const; + + enum + { + MAX_HEADER_LINE_LENGTH = 8192 + }; + + Headers_Map_Item &operator[] (const char *const header); + const Headers_Map_Item &operator[] (const char *const header) const; + +private: + int end_of_line (char *&line, int &offset) const; + +private: + Headers_Map map_; + int done_; +}; + +#endif /* PARSE_HEADERS_H */ diff --git a/ACE/apps/JAWS/server/README b/ACE/apps/JAWS/server/README new file mode 100644 index 00000000000..27f0a0e3a8d --- /dev/null +++ b/ACE/apps/JAWS/server/README @@ -0,0 +1,228 @@ +# -*- text -*- +# Hey, Emacs! This is a TEXT file. + +-------------------------- +README for the JAWS server +-------------------------- + +This is the README file for the JAWS server. + +CONTENTS + +1. Compiling + a. UNIX + b. Windows NT 4.0 + +2. Executing + a. svc.conf parameters + b. General Info + +3. Limitations + +4. Acknowledgements + +5. New additions + +------------ +1. Compiling +------------ + +1a. Compiling under UNIX. + + Assuming that the environment variable ACE_ROOT is set +correctly, and that you are using GNU make, compiling should simply +involve: + + $ cd $ACE_ROOT/apps/JAWS/server + $ make clean + $ make depend + $ make + +This will produce an executable named ``main''. + +1b. Compiling under Windows NT 4.0. + + Assuming you are using a recent version of Microsoft +Visual C++, you can use the jaws.mdp file located in +$ACE_ROOT/apps/JAWS/server to build JAWS. + + +------------ +2. Executing +------------ + +2a. svc.conf parameters. + + To run JAWS, simply execute "main". It loads the HTTP server +from the file named in the ``svc.conf'' file. The ``svc.conf'' file +itself contains documentation about the load line. It supports the +following command-line arguments: + + -p port Start JAWS on the specified port. + -n num_threads Use num_threads as the maximum number of threads. + -f thr_flag Can be used multiple times to set thread + creation flags: + THR_BOUND -> bound threads + THR_DAEMON -> daemonized threads + THR_DETACHED -> detached threads + THR_NEW_LWP -> increase concurrency level + -t thr_strategy Use one of the following strategies: + POOL -> thread pool + PER_REQUEST -> thread-per-request + THROTTLE -> thread-per-request with throttling + -i io_strategy Use one of the following strategies: + SYNCH -> synchronous I/O + ASYNCH -> asynchronous I/O + -b backlog Backlog value for listen (). + +2b. General Information + + By default, JAWS will used port 5432 with 5 threads and apply +the synchronous thread pool strategy. Unless set, the default backlog +value is equal the value of the maximum number of threads. + + JAWS also responds to several environment variables. This is +a temporary feature which will be replaced by a more general +configuration file similar to NCSA httpd's. The environment variables +are: + JAWS_DOCUMENT_ROOT + This is the starting point the server will use to look + for files. + Default value: the current directory of the server. + + JAWS_CGI_PATH + This is intended to be a ``:'' delimited list of paths + (similar to your regular PATH environment variable) which + describes the possible locations for CGI binaries. + Default value: Any directory named ``cgi-bin''. + + JAWS_USER_DIR + This is the name of the subdirectory in a users home + directory which contains publicly available WWW documents. + Default value: ``.www''. + + JAWS_DIR_INDEX + The name of the file which is sent, if present, when the URL + leads to a directory. + Default value: ``index.html''. + + You may test the server by executing telnet, opening a +connection to the server machine on the server port. For instance: + + $ telnet machinename 5432 + Trying ###.###.###.###... + Connected to machinename.your.domain + Escape character is '^]'. + GET /main.cpp + // main.cpp + //... + + Note that if you use an HTTP/1.0 get request, then you have +to hit return twice before the file will be sent. E.g., + + $ telnet machinename 5432 + Trying ###.###.###.###... + Connected to machinename.your.domain + Escape character is '^]'. + GET /main.cpp HTTP/1.0 + + // main.cpp + //... + + Where applicable, JAWS will perform ``~'' expansion for home +directories of usernames. + + +----------- +3. Features +----------- + +(a) JAWS supports full HTTP/1.0 responses. + +(b) JAWS support CGI scripts on UNIX. + +(c) JAWS parses request headers. The mechanism can be used to parse + headers from a variety of text based protocols (e.g., SNMP and + NNTP). + +(d) Optimized filecaching. + +-------------- +4. Limitations +-------------- + +The following are TODO items for JAWS: + +status|task +------+----------------------- + | (a) JAWS will support HTTP/1.1 eventually, including + | persistent connections. + | + | (b) JAWS can be more aggressive with its threading strategies, + | such as: + | (*) parallelize HTTP protocol processing, similar to + | PHTTPD. + | (*) prioritize threads to give more important requests + | more execution time. + | (*) apply throttling, similar to THTTPD. + | + | (c) JAWS will support a general protocol content filtering + | mechanism which will be used to replace the existing CGI + | support implementation. + | + +Questions, comments, corrections, suggestions are welcome. Please +feel free to send me any such correspondence. + +James Hu <jxh@cs.wustl.edu> + +------------------- +4. Acknowledgements +------------------- + + My partners in crime for this endeavor include: + + Eastman-Kodak, Rochester N.Y. + and Object Technologies, Inc. For providing funding for this + research. + + Dr. Douglas Schmidt For being my advisor, and + convincing my sponsors to fund + me. + + Irfan Pyarali For porting JAWS to NT, and + for designing and implementing + the JAWS IO mechanism. + + Sumedh Mungee For writing the benchmark + client, and performing the + UNIX benchmarks. + + Tim Harrison For his comments, advice, and + help in designing the IO + mechanism used by JAWS. + +----------------------- +5. Additions +----------------------- + +The need arose to have JAWS not perform any file caching. We added this +functionality and provided a new cmd line option -c with params NO_CACHE/CACHE. + +This capability is to be used with the RepositoryManager in CIAO. + +In its current design the RepositoryManager (RM) need a collocated HTTP server. +When RM istalls packages, it unpacks them so that the separate files are accessible to +ZIP unaware entities like JAWS. JAWS is used to serve the libraries in the package to the +various deamons that might be interested in them, e.g. the NodeApplicationManager. + +The problem with using file caching reveals itself during the deletePackage operation of +the RM. When the RM attempts to delete a file which was previously accessed via JAWS a +is currently in the file cache the call fails and the file remains on the filesystem +indefinitely. If the file is cached with a ACE_NOMAP option is is not stored in a file +map and it is deleted upon server termination. The OS handles that. Althoguh this might +or might not be OK (depending on how it scales) there is an additional problem because +JAWS and the ACE_Filecache_Handle class used do not provide enough functionality to deal +with the ACE_NOMAP case. I believe that ACE_NOMAP option was probably never used. + +To overcome the above problems we added the no caching functionality in JAWS. diff --git a/ACE/apps/JAWS/server/jaws.auth b/ACE/apps/JAWS/server/jaws.auth new file mode 100644 index 00000000000..e3c51f7eac8 --- /dev/null +++ b/ACE/apps/JAWS/server/jaws.auth @@ -0,0 +1,2 @@ +jxh:nonsense +bill:no nonsense diff --git a/ACE/apps/JAWS/server/main.cpp b/ACE/apps/JAWS/server/main.cpp new file mode 100644 index 00000000000..4f319f02727 --- /dev/null +++ b/ACE/apps/JAWS/server/main.cpp @@ -0,0 +1,57 @@ +// $Id$ + +#include "ace/Service_Config.h" +#include "ace/Reactor.h" +#include "ace/Filecache.h" + +#include "HTTP_Server.h" +#include "ace/OS_main.h" +#include "ace/OS_NS_signal.h" + +ACE_RCSID(server, main, "$Id$") + +ACE_STATIC_SVC_REQUIRE(HTTP_Server) + +#ifdef ACE_HAS_SIG_C_FUNC +extern "C" +{ +#endif /* ACE_HAS_SIG_C_FUNC */ + + // call exit() so that static destructors get called +static void +handler (int) +{ + delete (ACE_Filecache *) ACE_Filecache::instance (); + ACE_OS::exit (0); +} + +#ifdef ACE_HAS_SIG_C_FUNC +} +#endif /* ACE_HAS_SIG_C_FUNC */ + +// This is the driver entry point into JAWS. It is possible to use +// JAWS as an ACE Service, as well. + +int +main (int argc, char *argv[]) +{ + ACE_Service_Config daemon; + + ACE_OS::signal (SIGCHLD, SIG_IGN); + + // SigAction not needed since the handler will shutdown the server. + ACE_OS::signal (SIGINT, (ACE_SignalHandler) handler); + ACE_OS::signal (SIGUSR2, (ACE_SignalHandler) handler); + + if (daemon.open (argc, argv, ACE_DEFAULT_LOGGER_KEY, 0) != 0) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "open"), 1); + + // The configured service creates threads, and the + // server won't exit until the threads die. + + // Run forever, performing the configured services until we receive + // a SIGINT. + + + return 0; +} diff --git a/ACE/apps/JAWS/server/server.mpc b/ACE/apps/JAWS/server/server.mpc new file mode 100644 index 00000000000..a79e488cf83 --- /dev/null +++ b/ACE/apps/JAWS/server/server.mpc @@ -0,0 +1,36 @@ +// -*- MPC -*- +// $Id$ + +project(JAWS) : acelib { + sharedname = JAWS + dynamicflags = ACE_BUILD_SVC_DLL + requires += ace_filecache + avoids += uses_wchar + avoids += ace_for_tao + + Source_Files { + HTTP_Server.cpp + HTTP_Config.cpp + HTTP_Handler.cpp + HTTP_Helpers.cpp + JAWS_Pipeline.cpp + JAWS_Concurrency.cpp + HTTP_Request.cpp + HTTP_Response.cpp + Parse_Headers.cpp + IO.cpp + } +} + +project(JAWS_server) : aceexe { + exename = main + after += JAWS + libs += JAWS + requires += ace_filecache + avoids += uses_wchar + avoids += ace_for_tao + + Source_Files { + main.cpp + } +} diff --git a/ACE/apps/JAWS/server/svc.conf b/ACE/apps/JAWS/server/svc.conf new file mode 100644 index 00000000000..fdba6ab9c76 --- /dev/null +++ b/ACE/apps/JAWS/server/svc.conf @@ -0,0 +1,49 @@ +# +# -p port number +# -n threads in the server +# -f thread activation flags +# = THR_BOUND +# = THR_DAEMON +# = THR_DETACHED +# = THR_NEW_LWP +# -t threading strategy +# = POOL -> thread pool +# = PER_REQUEST -> thread per request +# = THROTTLE -> thread per request with throttling +# -i I/O strategy +# = SYNCH +# = ASYNCH +# -b backlog value for listen () +# -c caching: NO_CACHE or CACHE +# +# +# Thread Pool, 20 unbound threads +# This is the baseline +static HTTP_Server "HTTP_Server -p 5432 -n 20 -i SYNCH -t POOL -b 50 -f THR_NEW_LWP" +# +# +# Start a baseline server without caching +#static HTTP_Server "HTTP_Server -p 5432 -n 20 -i SYNCH -t POOL -b 50 -f THR_NEW_LWP -c NO_CACHE" +# +# +# Thread Pool, 40 threads +#static HTTP_Server "HTTP_Server -p 5432 -n 40 -i SYNCH -t POOL -b 50 -f THR_NEW_LWP -f THR_BOUND" +# +# +# Thread-per-request, unlimited number of threads +#static HTTP_Server "HTTP_Server -p 5432 -i SYNCH -t PER_REQUEST -b 50 -f THR_NEW_LWP" +# +# +# Throttling, 40 threads +#static HTTP_Server "HTTP_Server -p 5432 -n 40 -i SYNCH -t THROTTLE -b 50 -f THR_NEW_LWP" +# + +# +# Example for using HTTP_Server as a dynamic service +# +# For NT. +#dynamic HTTP_Server Service_Object * ./jaws.exe:_make_HTTP_Server() "HTTP_Server -p 5432 -n 20 -i SYNCH -t POOL -b 50 -f THR_NEW_LWP" + +# +# For UNIX platforms. +#dynamic HTTP_Server Service_Object * ./main:_make_HTTP_Server() "HTTP_Server -p 5432 -n 20 -i SYNCH -t POOL -b 50 -f THR_NEW_LWP" diff --git a/ACE/apps/JAWS/server/test.cgi b/ACE/apps/JAWS/server/test.cgi new file mode 100755 index 00000000000..936afcf0d3c --- /dev/null +++ b/ACE/apps/JAWS/server/test.cgi @@ -0,0 +1,9 @@ +#!/bin/sh + +echo Content-type: text/plain +echo + +echo args -- $* +env +echo Done! +exit 0 |