Diff for /parser3/src/targets/cgi/parser3.C between versions 1.90 and 1.145

version 1.90, 2001/05/24 09:29:52 version 1.145, 2001/12/05 11:26:42
Line 2 Line 2
         Parser: scripting and CGI main.          Parser: scripting and CGI main.
   
         Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)          Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)
           Author: Alexander Petrosyan <paf@design.ru>(http://paf.design.ru)
         Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf)  
   
         $Id$          $Id$
 */  */
Line 14 Line 13
 #       include <windows.h>  #       include <windows.h>
 #endif  #endif
   
   #if _MSC_VER
   #       include <new.h>
   #endif
   
 #include "pa_sapi.h"  #include "pa_sapi.h"
 #include "classes.h"  #include "classes.h"
 #include "pa_common.h"  #include "pa_common.h"
 #include "pa_request.h"  #include "pa_request.h"
 #include "pa_socks.h"  #include "pa_socks.h"
 #include "pa_version.h"  #include "pa_version.h"
   #include "pool_storage.h"
   
   #ifdef XML
   #include <XalanTransformer/XalanCAPI.h>
   #endif
   
 //#define DEBUG_POOL_MALLOC  //#define DEBUG_POOL_MALLOC
   
   // consts
   
   extern const char *main_RCSIds[];
   #ifdef USE_SMTP
   extern const char *smtp_RCSIds[];
   #endif
   extern const char *gd_RCSIds[];
   extern const char *classes_RCSIds[];
   extern const char *types_RCSIds[];
   extern const char *parser3_RCSIds[];
   #ifdef XML
   extern const char *xalan_patched_RCSIds[];
   #endif
   const char **RCSIds[]={
           main_RCSIds,
   #ifdef USE_SMTP
           smtp_RCSIds,
   #endif
           gd_RCSIds,
           classes_RCSIds,
           types_RCSIds,
           parser3_RCSIds,
   #ifdef XML
           xalan_patched_RCSIds,
   #endif
           0
   };
   
 /// IIS refuses to read bigger chunks  /// IIS refuses to read bigger chunks
 const size_t READ_POST_CHUNK_SIZE=0x400*0x400; // 1M   const size_t READ_POST_CHUNK_SIZE=0x400*0x400; // 1M 
   
 const char *argv0;  const char *argv0;
 Pool pool(0); // global pool [dont describe to doxygen: it confuses it with param names]  Pool_storage pool_storage;
   Pool pool(&pool_storage); // global pool [dont describe to doxygen: it confuses it with param names]
 bool cgi; ///< we were started as CGI?  bool cgi; ///< we were started as CGI?
   
 #ifdef WIN32  
 /// global system errors into parser exceptions converter  
 static LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo) {  
         char buf[MAX_STRING];  
         if(ExceptionInfo && ExceptionInfo->ExceptionRecord) {  
                 struct _EXCEPTION_RECORD *er=ExceptionInfo->ExceptionRecord;  
                 snprintf(buf, MAX_STRING, "Exception 0x%X at %p",   
                         er->ExceptionCode,   
                         er->ExceptionAddress);  
         } else   
                 strcpy(buf, "Exception <unknown>");  
           
         PTHROW(0, 0,  
                 0,  
                 buf);  
   
         return EXCEPTION_EXECUTE_HANDLER; // never reached  
 }  
 #endif  
   
 // SAPI  // SAPI
   
 // appends to parser3.log located beside my binary if openable, to stderr otherwize  static void log(const char *fmt, va_list args) {
 void SAPI::log(Pool& pool, const char *fmt, ...) {  
         bool opened;          bool opened;
         FILE *f=0;          FILE *f=0;
   
         if(argv0) {          if(argv0) {
                 // beside by binary                  // beside by binary
                 char file_spec[MAX_STRING];                  char file_spec[MAX_STRING];
                 strncpy(file_spec, argv0, MAX_STRING);  // filespec of my binary                  strncpy(file_spec, argv0, MAX_STRING-1);  file_spec[MAX_STRING-1]=0; // filespec of my binary
                 rsplit(file_spec, '/');  rsplit(file_spec, '\\');// strip filename                  rsplit(file_spec, '/');  rsplit(file_spec, '\\');// strip filename
                 strcat(file_spec, "/parser3.log");                  strcat(file_spec, "/parser3.log");
                 f=fopen(file_spec, "at");                  f=fopen(file_spec, "at");
Line 75  void SAPI::log(Pool& pool, const char *f Line 90  void SAPI::log(Pool& pool, const char *f
         const char *stamp=ctime(&t);          const char *stamp=ctime(&t);
         fprintf(f, "[%.*s] ", strlen(stamp)-1, stamp);          fprintf(f, "[%.*s] ", strlen(stamp)-1, stamp);
         // message          // message
     va_list args;  
         va_start(args,fmt);          char buf[MAX_STRING];
         vfprintf(f, fmt, args);          size_t size=vsnprintf(buf, MAX_STRING, fmt, args);
         va_end(args);          remove_crlf(buf, buf+size);
   
           fwrite(buf, size, 1, f);
         // newline          // newline
         fprintf(f, "\n");          fprintf(f, "\n");
   
Line 88  void SAPI::log(Pool& pool, const char *f Line 105  void SAPI::log(Pool& pool, const char *f
                 fflush(f);                  fflush(f);
 }  }
   
 const char *SAPI::get_env(Pool& pool, const char *name) {  // appends to parser3.log located beside my binary if openable, to stderr otherwize
         return getenv(name);  void SAPI::log(Pool& , const char *fmt, ...) {
       va_list args;
           va_start(args,fmt);
           ::log(fmt, args);
           va_end(args);
   }
   
   void SAPI::die(const char *fmt, ...) {
   #ifdef DEBUG_POOL_MALLOC
           extern void log_pool_stats(Pool& pool);
           log_pool_stats(pool);
   #endif
   
       va_list args;
           va_start(args,fmt);
           // log
   
           // logging is more important than user 
           // she can cancel download, we'd get SIG_PIPE, 
           // nothing would be logged then
           ::log(fmt, args);
   
           // inform user
   
           char body[MAX_STRING];
           int content_length=vsnprintf(body, MAX_STRING, fmt, args);
   
           va_end(args);
   
           // prepare header
           // let's be honest, that's bad we couldn't produce valid output
           SAPI::add_header_attribute(pool, "status", "500");
           SAPI::add_header_attribute(pool, "content-type", "text/plain");
           char content_length_cstr[MAX_NUMBER];
           snprintf(content_length_cstr, MAX_NUMBER, "%u", content_length);
           SAPI::add_header_attribute(pool, "content-length", content_length_cstr);
   
           // send header
           SAPI::send_header(pool);
   
           // body
           SAPI::send_body(pool, body, content_length);
   
           exit(1);
   }
   
   const char *SAPI::get_env(Pool& , const char *name) {
           return getenv(name);
 }  }
   
 size_t SAPI::read_post(Pool& pool, char *buf, size_t max_bytes) {  size_t SAPI::read_post(Pool& , char *buf, size_t max_bytes) {
         size_t read_size=0;          size_t read_size=0;
         do {          do {
                 int chunk_size=read(fileno(stdin),                   int chunk_size=read(fileno(stdin), 
                         buf+read_size, min(READ_POST_CHUNK_SIZE, max_bytes-read_size));                          buf+read_size, min(READ_POST_CHUNK_SIZE, max_bytes-read_size));
                 if(chunk_size<0)                  if(chunk_size<=0)
                         break;                          break;
                 read_size+=chunk_size;                  read_size+=chunk_size;
         } while(read_size<max_bytes);          } while(read_size<max_bytes);
Line 105  size_t SAPI::read_post(Pool& pool, char Line 169  size_t SAPI::read_post(Pool& pool, char
         return read_size;          return read_size;
 }  }
   
 void SAPI::add_header_attribute(Pool& pool, const char *key, const char *value) {  void SAPI::add_header_attribute(Pool& , const char *key, const char *value) {
         if(cgi)          if(cgi)
                 printf("%s: %s\n", key, value);                  printf("%s: %s\n", key, value);
 }  }
   
 /// @todo intelligent cache-control  /// @todo intelligent cache-control
 void SAPI::send_header(Pool& pool) {  void SAPI::send_header(Pool& ) {
         if(cgi) {          if(cgi) {
                 puts("expires: Fri, 23 Mar 2001 09:32:23 GMT");                  puts("expires: Fri, 23 Mar 2001 09:32:23 GMT");
   
Line 120  void SAPI::send_header(Pool& pool) { Line 184  void SAPI::send_header(Pool& pool) {
         }          }
 }  }
   
 void SAPI::send_body(Pool& pool, const void *buf, size_t size) {  void SAPI::send_body(Pool& , const void *buf, size_t size) {
         stdout_write(buf, size);          stdout_write(buf, size);
 }  }
   
   //
   
   char *full_file_spec(char *file_name) {
           if(file_name && !strchr(file_name, '/')) {
                   static char cwd[MAX_STRING];  getcwd(cwd, MAX_STRING);
                   static char buf[MAX_STRING];
                   snprintf(buf, MAX_STRING, "%s/%s", cwd, file_name);
                   return buf;
           }
           return file_name;
   }
   
 /**  /**
         main workhorse  main workhorse
   
         @todo     @todo 
                 IIS: remove trailing default-document[index.html] from $request.uri.                  IIS: remove trailing default-document[index.html] from $request.uri.
                 to do that we need to consult metabase,                  to do that we need to consult metabase,
                 wich is tested but seems slow.                  wich is tested but seems slow.
                   IIS5 todo find out proper 'illegal call' check 
 */  */
   void real_parser_handler(
                                           const char *filespec_to_process,
                                           const char *request_method, bool header_only) {
           // init socks
           init_socks(pool);
           
   #ifdef XML
           /**
           * Initialize Xerces and Xalan.
           *
           * Should be called only once per process before making
           * any other API calls.
           */
           //_asm int 3;
           XalanInitialize();
   #endif
           
           // init global classes
           init_methoded_array(pool);
           // init global variables
           pa_globals_init(pool);
           
           if(!filespec_to_process)
                   SAPI::die("Parser/%s", PARSER_VERSION);
           
           // Request info
           Request::Info request_info;
           if(cgi) {
                   if(const char *env_document_root=SAPI::get_env(pool, "DOCUMENT_ROOT"))
                           request_info.document_root=env_document_root;
                   else if(const char *path_info=SAPI::get_env(pool, "PATH_INFO")) {
                           // IIS
                           size_t len=strlen(filespec_to_process)-strlen(path_info);
                           char *buf=(char *)pool.malloc(len+1);
                           memcpy(buf, filespec_to_process, len); buf[len]=0;
                           request_info.document_root=buf;
                   } else
                           throw Exception(0, 0,
                           0,
                           "CGI: no PATH_INFO defined(in reinventing DOCUMENT_ROOT)");
           } else {
                   static char buf[MAX_STRING];
                   strncpy(buf, filespec_to_process, MAX_STRING-1); buf[MAX_STRING-1]=0;
                   if(rsplit(buf, '/') || rsplit(buf, '\\')) // strip filename
                           request_info.document_root=buf;
                   else
                           request_info.document_root="";
           }
           request_info.path_translated=filespec_to_process;
           request_info.method=request_method ? request_method : "GET";
           const char *query_string=SAPI::get_env(pool, "QUERY_STRING");
           request_info.query_string=query_string;
           if(cgi) {
                   if(const char *env_request_uri=SAPI::get_env(pool, "REQUEST_URI"))
                           request_info.uri=env_request_uri;
                   else if(const char *path_info=SAPI::get_env(pool, "PATH_INFO"))
                           if(query_string) {
                                   char *reconstructed_uri=(char *)pool.malloc(
                                           strlen(path_info)+1/*'?'*/+
                                           strlen(query_string)+1/*0*/);
                                   strcpy(reconstructed_uri, path_info);
                                   strcat(reconstructed_uri, "?");
                                   strcat(reconstructed_uri, query_string);
                                   request_info.uri=reconstructed_uri;
                           } else
                                   request_info.uri=path_info;
                           else
                                   throw Exception(0, 0,
                                   0,
                                   "CGI: no PATH_INFO defined(in reinventing REQUEST_URI)");
                           
   #ifndef WIN32
                           // they've changed this under IIS5.
                           if(const char *script_name=SAPI::get_env(pool, "SCRIPT_NAME")) {
                                   size_t script_name_len=strlen(script_name);
                                   size_t uri_len=strlen(request_info.uri);
                                   if(strncmp(request_info.uri,script_name, script_name_len)==0 &&
                                           script_name_len != uri_len) // under IIS they are the same
                                           SAPI::die("CGI: illegal call");
                           }
   #endif
           } else
                   request_info.uri=0;
           
           request_info.content_type=SAPI::get_env(pool, "CONTENT_TYPE");
           const char *content_length=SAPI::get_env(pool, "CONTENT_LENGTH");
           request_info.content_length=(content_length?atoi(content_length):0);
           request_info.cookie=SAPI::get_env(pool, "HTTP_COOKIE");
           request_info.user_agent=SAPI::get_env(pool, "HTTP_USER_AGENT");
           
           // prepare to process request
           Request request(pool,
                   request_info,
   #ifdef _DEBUG
                   String::UL_HTML|String::UL_OPTIMIZE_BIT
   #else
                   cgi ? String::UL_HTML|String::UL_OPTIMIZE_BIT : String::UL_AS_IS
   #endif
                   ,
                   true /* status_allowed */);
           
           // some root-controlled location
   #ifdef SYSCONFDIR
           const char *root_config_filespec=SYSCONFDIR "/" CONFIG_FILE_NAME;
   #else
   #       ifdef WIN32
           // c:\windows
           char root_config_path[MAX_STRING];
           GetWindowsDirectory(root_config_path, MAX_STRING);
           
           char root_config_filespec[MAX_STRING];
           snprintf(root_config_filespec, MAX_STRING, 
                   "%s/%s", 
                   root_config_path, CONFIG_FILE_NAME);
   #       else
   #error must be compiled either configure/make or MSVC++
   #       endif
   #endif
           
           // beside by binary
           // @todo full path, not ./!
           static char site_config_path[MAX_STRING];
           strncpy(site_config_path, argv0, MAX_STRING-1);  site_config_path[MAX_STRING-1]=0; // filespec of my binary
           if(!(
                   rsplit(site_config_path, '/') || 
                   rsplit(site_config_path, '\\'))) { // strip filename
                   // no path, just filename
                   site_config_path[0]='.'; site_config_path[1]=0;
           }
           
           char site_config_filespec[MAX_STRING];
           snprintf(site_config_filespec, MAX_STRING, 
                   "%s/%s", 
                   site_config_path, CONFIG_FILE_NAME);
           
           // process the request
           request.core(
                   root_config_filespec, false,
                   site_config_filespec, false,
                   header_only);
           
           //
           done_socks();
           
   #ifdef DEBUG_POOL_MALLOC
           extern void log_pool_stats(Pool& pool);
           log_pool_stats(pool);
   #endif
   }
   
   void call_real_parser_handler__do_SEH(
                                                                    const char *filespec_to_process,
                                                                    const char *request_method, bool header_only) {
   #if _MSC_VER && !defined(_DEBUG)
           LPEXCEPTION_POINTERS system_exception=0;
           __try {
   #endif
                   real_parser_handler(
                           filespec_to_process,
                           request_method, header_only);
                   
   #if _MSC_VER && !defined(_DEBUG)
           } __except (
                   (system_exception=GetExceptionInformation()), 
                   EXCEPTION_EXECUTE_HANDLER) {
                   
                   if(system_exception)
                           if(_EXCEPTION_RECORD *er=system_exception->ExceptionRecord)
                                   throw Exception(0, 0,
                                   0,
                                   "Exception 0x%08X at 0x%08X", er->ExceptionCode,  er->ExceptionAddress);
                           else
                                   throw Exception(0, 0, 0, "Exception <no exception record>");
                           else
                                   throw Exception(0, 0, 0, "Exception <no exception information>");
           }
   #endif
   }
   
   #if _MSC_VER
   int failed_new(size_t size) {
           SAPI::die("out of memory in 'new', failed to allocated %u bytes", size);
           return 0; // not reached
   }
   #endif
   
   #ifdef HAVE_SET_NEW_HANDLER
   void failed_new() {
       SAPI::die("out of memory in 'new'");
   }
   #endif
   
 int main(int argc, char *argv[]) {  int main(int argc, char *argv[]) {
   //      _asm int 3;
         argv0=argv[0];          argv0=argv[0];
   
         umask(2);          umask(2);
   
 #ifdef WIN32  
         setmode(fileno(stdin), _O_BINARY);  
         setmode(fileno(stdout), _O_BINARY);  
         setmode(fileno(stderr), _O_BINARY);  
 #endif  
   
         // were we started as CGI?          // were we started as CGI?
         cgi=          cgi=1||
                 getenv("SERVER_SOFTWARE") ||                   getenv("SERVER_SOFTWARE") || 
                 getenv("SERVER_NAME") ||                   getenv("SERVER_NAME") || 
                 getenv("GATEWAY_INTERFACE") ||                   getenv("GATEWAY_INTERFACE") || 
Line 154  int main(int argc, char *argv[]) { Line 418  int main(int argc, char *argv[]) {
                 if(argc<2) {                  if(argc<2) {
                         printf(                          printf(
                                 "Parser/%s Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)\n"                                  "Parser/%s Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)\n"
                                 "Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf)\n"                                  "Author: Alexander Petrosyan <paf@design.ru>(http://paf.design.ru)\n"
                                 "\n"                                  "\n"
                                 "Usage: %s <file>\n",                                  "Usage: %s <file>\n",
                                 PARSER_VERSION,                                   PARSER_VERSION, 
Line 163  int main(int argc, char *argv[]) { Line 427  int main(int argc, char *argv[]) {
                 }                  }
         }          }
   
         char *filespec_to_process=cgi?getenv("PATH_TRANSLATED"):argv[1];  
 #ifdef WIN32  #ifdef WIN32
         back_slashes_to_slashes(filespec_to_process);          setmode(fileno(stdin), _O_BINARY);
           setmode(fileno(stdout), _O_BINARY);
           setmode(fileno(stderr), _O_BINARY);
 #endif  #endif
   
         const char *request_method=getenv("REQUEST_METHOD");  #if _MSC_VER
         bool header_only=request_method && strcasecmp(request_method, "HEAD")==0;          _set_new_handler(failed_new);
         PTRY { // global try  
                 // must be first in PTRY{}PCATCH  
 #ifdef WIN32  
                 SetUnhandledExceptionFilter(&TopLevelExceptionFilter);  
 #endif  #endif
   
                 // init socks  #ifdef HAVE_SET_NEW_HANDLER
                 init_socks(pool);          std::set_new_handler(failed_new);
   #endif
                 // init global classes  
                 init_methoded_array(pool);  
                 // init global variables  
                 pa_globals_init(pool);  
   
                 if(!filespec_to_process)  
                         PTHROW(0, 0,  
                                 0,  
                                 "Parser/%s", PARSER_VERSION);  
   
                 // Request info  
                 Request::Info request_info;  
                 if(cgi) {  
                         if(const char *env_document_root=SAPI::get_env(pool, "DOCUMENT_ROOT"))  
                                 request_info.document_root=env_document_root;  
                         else if(const char *path_info=SAPI::get_env(pool, "PATH_INFO")) {  
                                 // IIS  
                                 size_t len=strlen(filespec_to_process)-strlen(path_info);  
                                 char *buf=(char *)pool.malloc(len+1);  
                                 strncpy(buf, filespec_to_process, len);  
                                 buf[len]=0;  
                                 request_info.document_root=buf;  
                         } else  
                                 PTHROW(0, 0,  
                                         0,  
                                         "CGI: no PATH_INFO defined(in reinventing DOCUMENT_ROOT)");  
                 } else {  
                         static char buf[MAX_STRING];  
                         strncpy(buf, filespec_to_process, MAX_STRING);  
                         if(rsplit(buf, '/') || rsplit(buf, '\\')) // strip filename  
                                 request_info.document_root=buf;  
                         else  
                                 request_info.document_root="";  
                 }  
                 request_info.path_translated=filespec_to_process;  
                 request_info.method=request_method;  
                 const char *query_string=SAPI::get_env(pool, "QUERY_STRING");  
                 request_info.query_string=query_string;  
                 if(cgi) {  
                         if(const char *env_request_uri=SAPI::get_env(pool, "REQUEST_URI"))  
                                 request_info.uri=env_request_uri;  
                         else if(const char *path_info=SAPI::get_env(pool, "PATH_INFO"))  
                                 if(query_string) {  
                                         char *reconstructed_uri=(char *)malloc(  
                                                 strlen(path_info)+1/*'?'*/+  
                                                 strlen(query_string)+1/*0*/);  
                                         strcpy(reconstructed_uri, path_info);  
                                         strcat(reconstructed_uri, "?");  
                                         strcat(reconstructed_uri, query_string);  
                                         request_info.uri=reconstructed_uri;  
                                 } else  
                                         request_info.uri=path_info;  
                         else  
                                 PTHROW(0, 0,  
                                         0,  
                                         "CGI: no PATH_INFO defined(in reinventing REQUEST_URI)");  
   
                         if(const char *script_name=SAPI::get_env(pool, "SCRIPT_NAME")) {  
                                 size_t script_name_len=strlen(script_name);  
                                 size_t uri_len=strlen(request_info.uri);  
                                 if(strncmp(request_info.uri,script_name, script_name_len)==0 &&  
                                         script_name_len != uri_len) // under IIS they are the same  
                                         PTHROW(0, 0,  
                                                 0,  
                                                 "CGI: illegal call");  
                         }  
                 } else  
                         request_info.uri=0;  
   
                 request_info.content_type=SAPI::get_env(pool, "CONTENT_TYPE");          char *filespec_to_process=cgi?getenv("PATH_TRANSLATED"):argv[1];
                 const char *content_length=SAPI::get_env(pool, "CONTENT_LENGTH");  
                 request_info.content_length=(content_length?atoi(content_length):0);  
                 request_info.cookie=SAPI::get_env(pool, "HTTP_COOKIE");  
                 request_info.user_agent=SAPI::get_env(pool, "HTTP_USER_AGENT");  
   
                 // prepare to process request  
                 Request request(pool,  
                         request_info,  
                         cgi||1 ? String::UL_USER_HTML : String::UL_AS_IS  
                         );  
                   
                 // some root-controlled location  
 #ifdef WIN32  #ifdef WIN32
                 // c:\windows          back_slashes_to_slashes(filespec_to_process);
                 static char root_auto_path[MAX_STRING];  
                 GetWindowsDirectory(root_auto_path, MAX_STRING);  
 #else  
                 // ~nobody  todo: figure out a better place  
                 const char *root_auto_path=SAPI::get_env(pool, "HOME");  
 #endif  #endif
                           filespec_to_process=full_file_spec(filespec_to_process);
                 // beside by binary  
                 static char site_auto_path[MAX_STRING];  
                 strncpy(site_auto_path, argv0, MAX_STRING);  // filespec of my binary  
                 if(!(  
                         rsplit(site_auto_path, '/') ||   
                         rsplit(site_auto_path, '\\'))) { // strip filename  
                         // no path, just filename  
                         site_auto_path[0]='.'; site_auto_path[1]=0;  
                 }  
                   
                 // process the request  
                 request.core(  
                         root_auto_path, false,  
                         site_auto_path, false,  
                         header_only);  
   
                 //          const char *request_method=getenv("REQUEST_METHOD");
                 done_socks();          bool header_only=request_method && strcasecmp(request_method, "HEAD")==0;
   
 #ifdef DEBUG_POOL_MALLOC  
                 extern void log_pool_stats(Pool& pool);  
                 log_pool_stats(pool);  
 #endif  
   
                 // must be last in PTRY{}PCATCH          try { // global try
 #ifdef WIN32                  call_real_parser_handler__do_SEH(
                 SetUnhandledExceptionFilter(0);                          filespec_to_process,
 #endif                          request_method, header_only);
                 // successful finish          } catch(const Exception& e) { // global problem 
                 return 0;  
         } PCATCH(e) { // global problem   
                 // must be first in PCATCH{}  
 #ifdef WIN32  
                 SetUnhandledExceptionFilter(0);  
 #endif  
                 // don't allocate anything on pool here:                  // don't allocate anything on pool here:
                 //   possible pool' exception not catch-ed now                  //   possible pool' exception not catch-ed now
                 //   and there could be out-of-memory exception                  //   and there could be out-of-memory exception
   
                 const char *body=e.comment();                  SAPI::die("exception in request exception handler: %s", e.comment());
                 // log it  #ifndef _DEBUG
                 SAPI::log(pool, "exception in request exception handler: %s", body);          } catch(...) { 
                   SAPI::die("<unknown exception>");
                 //  #endif
                 int content_length=strlen(body);  
   
                 // prepare header  
                 SAPI::add_header_attribute(pool, "content-type", "text/plain");  
                 char content_length_cstr[MAX_NUMBER];  
                 snprintf(content_length_cstr, MAX_NUMBER, "%u", content_length);  
                 SAPI::add_header_attribute(pool, "content-length", content_length_cstr);  
   
                 // send header  
                 SAPI::send_header(pool);  
   
                 // body  
                 if(!header_only)  
                         SAPI::send_body(pool, body, content_length);  
   
                 // unsuccessful finish  
                 return 1;  
         }          }
         PEND_CATCH          
   
   #ifndef WIN32
           // 
           if(!cgi)
                   SAPI::send_body(pool, "\n", 1);
   #endif
           return 0;
 }  }

Removed from v.1.90  
changed lines
  Added in v.1.145


E-mail: