Annotation of parser3/src/targets/cgi/parser3.C, revision 1.99
1.27 paf 1: /** @file
2: Parser: scripting and CGI main.
3:
1.43 paf 4: Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)
1.27 paf 5:
1.43 paf 6: Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf)
1.1 paf 7: */
1.99 ! parser 8: static const char *RCSId="$Id: parser3.C,v 1.98 2001/08/28 10:30:29 parser Exp $";
1.1 paf 9:
1.40 paf 10: #include "pa_config_includes.h"
1.3 paf 11:
12: #ifdef WIN32
13: # include <windows.h>
14: #endif
1.27 paf 15:
1.37 paf 16: #include "pa_sapi.h"
1.76 paf 17: #include "classes.h"
1.24 paf 18: #include "pa_common.h"
1.2 paf 19: #include "pa_request.h"
1.57 paf 20: #include "pa_socks.h"
1.68 paf 21: #include "pa_version.h"
1.69 paf 22:
1.99 ! parser 23: #ifdef WIN32
! 24: # define EOL "\r\n"
! 25: #else
! 26: # define EOL "\n"
! 27: #endif
! 28:
1.84 parser 29: //#define DEBUG_POOL_MALLOC
30:
31:
1.42 paf 32: /// IIS refuses to read bigger chunks
33: const size_t READ_POST_CHUNK_SIZE=0x400*0x400; // 1M
34:
1.45 paf 35: const char *argv0;
1.42 paf 36: Pool pool(0); // global pool [dont describe to doxygen: it confuses it with param names]
1.27 paf 37: bool cgi; ///< we were started as CGI?
1.5 paf 38:
1.40 paf 39: #ifdef WIN32
40: /// global system errors into parser exceptions converter
1.43 paf 41: static LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo) {
1.5 paf 42: char buf[MAX_STRING];
43: if(ExceptionInfo && ExceptionInfo->ExceptionRecord) {
1.36 paf 44: struct _EXCEPTION_RECORD *er=ExceptionInfo->ExceptionRecord;
1.46 paf 45: snprintf(buf, MAX_STRING, "Exception 0x%X at %p",
1.31 paf 46: er->ExceptionCode,
47: er->ExceptionAddress);
1.5 paf 48: } else
49: strcpy(buf, "Exception <unknown>");
50:
51: PTHROW(0, 0,
52: 0,
53: buf);
54:
55: return EXCEPTION_EXECUTE_HANDLER; // never reached
56: }
1.27 paf 57: #endif
58:
1.46 paf 59: // SAPI
1.86 parser 60:
61: // appends to parser3.log located beside my binary if openable, to stderr otherwize
1.61 paf 62: void SAPI::log(Pool& pool, const char *fmt, ...) {
63: bool opened;
64: FILE *f=0;
65:
66: if(argv0) {
67: // beside by binary
68: char file_spec[MAX_STRING];
1.98 parser 69: strncpy(file_spec, argv0, MAX_STRING-1); file_spec[MAX_STRING-1]=0; // filespec of my binary
1.61 paf 70: rsplit(file_spec, '/'); rsplit(file_spec, '\\');// strip filename
71: strcat(file_spec, "/parser3.log");
72: f=fopen(file_spec, "at");
73: }
74: opened=f!=0;
75: if(!opened)
76: f=stderr;
77:
78: // prefix
79: time_t t=time(0);
80: const char *stamp=ctime(&t);
81: fprintf(f, "[%.*s] ", strlen(stamp)-1, stamp);
82: // message
83: va_list args;
84: va_start(args,fmt);
85: vfprintf(f, fmt, args);
86: va_end(args);
87: // newline
88: fprintf(f, "\n");
89:
90: if(opened)
91: fclose(f);
1.85 parser 92: else
93: fflush(f);
1.61 paf 94: }
95:
1.37 paf 96: const char *SAPI::get_env(Pool& pool, const char *name) {
1.28 paf 97: return getenv(name);
98: }
99:
1.59 paf 100: size_t SAPI::read_post(Pool& pool, char *buf, size_t max_bytes) {
101: size_t read_size=0;
1.12 paf 102: do {
1.36 paf 103: int chunk_size=read(fileno(stdin),
1.42 paf 104: buf+read_size, min(READ_POST_CHUNK_SIZE, max_bytes-read_size));
1.12 paf 105: if(chunk_size<0)
106: break;
107: read_size+=chunk_size;
108: } while(read_size<max_bytes);
109:
110: return read_size;
1.10 paf 111: }
112:
1.37 paf 113: void SAPI::add_header_attribute(Pool& pool, const char *key, const char *value) {
1.68 paf 114: if(cgi)
1.20 paf 115: printf("%s: %s\n", key, value);
1.19 paf 116: }
117:
1.56 paf 118: /// @todo intelligent cache-control
1.37 paf 119: void SAPI::send_header(Pool& pool) {
1.33 paf 120: if(cgi) {
1.55 paf 121: puts("expires: Fri, 23 Mar 2001 09:32:23 GMT");
1.33 paf 122:
123: // header | body delimiter
1.20 paf 124: puts("");
1.33 paf 125: }
1.30 paf 126: }
1.20 paf 127:
1.53 paf 128: void SAPI::send_body(Pool& pool, const void *buf, size_t size) {
1.19 paf 129: stdout_write(buf, size);
1.58 paf 130: }
131:
1.97 parser 132: //
133:
134: char *full_file_spec(char *file_name) {
135: if(!strchr(file_name, '/')) {
136: static char cwd[MAX_STRING]; getcwd(cwd, MAX_STRING);
137: static char buf[MAX_STRING];
138: snprintf(buf, MAX_STRING, "%s/%s", cwd, file_name);
139: return buf;
140: }
141: return file_name;
142: }
143:
1.40 paf 144: /**
145: main workhorse
1.19 paf 146:
1.56 paf 147: @todo
1.40 paf 148: IIS: remove trailing default-document[index.html] from $request.uri.
149: to do that we need to consult metabase,
150: wich is tested but seems slow.
151: */
1.5 paf 152: int main(int argc, char *argv[]) {
1.45 paf 153: argv0=argv[0];
154:
1.32 paf 155: umask(2);
156:
157: #ifdef WIN32
1.27 paf 158: setmode(fileno(stdin), _O_BINARY);
159: setmode(fileno(stdout), _O_BINARY);
160: setmode(fileno(stderr), _O_BINARY);
1.32 paf 161: #endif
1.12 paf 162:
1.3 paf 163: // were we started as CGI?
1.20 paf 164: cgi=
1.2 paf 165: getenv("SERVER_SOFTWARE") ||
166: getenv("SERVER_NAME") ||
167: getenv("GATEWAY_INTERFACE") ||
168: getenv("REQUEST_METHOD");
1.5 paf 169:
1.10 paf 170: if(!cgi) {
171: if(argc<2) {
1.69 paf 172: printf(
1.99 ! parser 173: "Parser/%s Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)"EOL
! 174: "Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf)"EOL
! 175: EOL
! 176: "Usage: %s <file>"EOL,
1.69 paf 177: PARSER_VERSION,
178: argv0?argv0:"parser3");
1.67 paf 179: return 1;
1.10 paf 180: }
181: }
182:
1.26 paf 183: char *filespec_to_process=cgi?getenv("PATH_TRANSLATED"):argv[1];
1.36 paf 184: #ifdef WIN32
1.43 paf 185: back_slashes_to_slashes(filespec_to_process);
1.36 paf 186: #endif
1.97 parser 187: filespec_to_process=full_file_spec(filespec_to_process);
1.10 paf 188:
1.35 paf 189: const char *request_method=getenv("REQUEST_METHOD");
190: bool header_only=request_method && strcasecmp(request_method, "HEAD")==0;
1.5 paf 191: PTRY { // global try
192: // must be first in PTRY{}PCATCH
1.40 paf 193: #ifdef WIN32
1.5 paf 194: SetUnhandledExceptionFilter(&TopLevelExceptionFilter);
195: #endif
1.2 paf 196:
1.62 paf 197: // init socks
1.57 paf 198: init_socks(pool);
199:
1.73 paf 200: // init global classes
201: init_methoded_array(pool);
1.10 paf 202: // init global variables
1.37 paf 203: pa_globals_init(pool);
1.10 paf 204:
1.13 paf 205: if(!filespec_to_process)
206: PTHROW(0, 0,
207: 0,
1.68 paf 208: "Parser/%s", PARSER_VERSION);
1.13 paf 209:
1.10 paf 210: // Request info
211: Request::Info request_info;
1.39 paf 212: if(cgi) {
1.51 paf 213: if(const char *env_document_root=SAPI::get_env(pool, "DOCUMENT_ROOT"))
1.39 paf 214: request_info.document_root=env_document_root;
1.51 paf 215: else if(const char *path_info=SAPI::get_env(pool, "PATH_INFO")) {
1.40 paf 216: // IIS
1.39 paf 217: size_t len=strlen(filespec_to_process)-strlen(path_info);
1.40 paf 218: char *buf=(char *)pool.malloc(len+1);
1.98 parser 219: memcpy(buf, filespec_to_process, len); buf[len]=0;
1.39 paf 220: request_info.document_root=buf;
221: } else
222: PTHROW(0, 0,
223: 0,
1.43 paf 224: "CGI: no PATH_INFO defined(in reinventing DOCUMENT_ROOT)");
1.39 paf 225: } else {
226: static char buf[MAX_STRING];
1.98 parser 227: strncpy(buf, filespec_to_process, MAX_STRING-1); buf[MAX_STRING-1]=0;
1.70 paf 228: if(rsplit(buf, '/') || rsplit(buf, '\\')) // strip filename
229: request_info.document_root=buf;
230: else
231: request_info.document_root="";
1.13 paf 232: }
1.10 paf 233: request_info.path_translated=filespec_to_process;
1.35 paf 234: request_info.method=request_method;
1.51 paf 235: const char *query_string=SAPI::get_env(pool, "QUERY_STRING");
1.40 paf 236: request_info.query_string=query_string;
1.68 paf 237: if(cgi) {
1.51 paf 238: if(const char *env_request_uri=SAPI::get_env(pool, "REQUEST_URI"))
1.42 paf 239: request_info.uri=env_request_uri;
1.51 paf 240: else if(const char *path_info=SAPI::get_env(pool, "PATH_INFO"))
1.42 paf 241: if(query_string) {
242: char *reconstructed_uri=(char *)malloc(
243: strlen(path_info)+1/*'?'*/+
244: strlen(query_string)+1/*0*/);
245: strcpy(reconstructed_uri, path_info);
246: strcat(reconstructed_uri, "?");
247: strcat(reconstructed_uri, query_string);
248: request_info.uri=reconstructed_uri;
249: } else
250: request_info.uri=path_info;
251: else
252: PTHROW(0, 0,
253: 0,
1.43 paf 254: "CGI: no PATH_INFO defined(in reinventing REQUEST_URI)");
1.68 paf 255:
1.72 paf 256: if(const char *script_name=SAPI::get_env(pool, "SCRIPT_NAME")) {
257: size_t script_name_len=strlen(script_name);
258: size_t uri_len=strlen(request_info.uri);
259: if(strncmp(request_info.uri,script_name, script_name_len)==0 &&
260: script_name_len != uri_len) // under IIS they are the same
261: PTHROW(0, 0,
262: 0,
263: "CGI: illegal call");
264: }
1.68 paf 265: } else
1.42 paf 266: request_info.uri=0;
1.40 paf 267:
1.51 paf 268: request_info.content_type=SAPI::get_env(pool, "CONTENT_TYPE");
269: const char *content_length=SAPI::get_env(pool, "CONTENT_LENGTH");
1.10 paf 270: request_info.content_length=(content_length?atoi(content_length):0);
1.51 paf 271: request_info.cookie=SAPI::get_env(pool, "HTTP_COOKIE");
272: request_info.user_agent=SAPI::get_env(pool, "HTTP_USER_AGENT");
1.10 paf 273:
1.5 paf 274: // prepare to process request
1.38 paf 275: Request request(pool,
1.10 paf 276: request_info,
1.91 parser 277: cgi ? String::UL_USER_HTML : String::UL_AS_IS
1.5 paf 278: );
279:
280: // some root-controlled location
1.3 paf 281: #ifdef WIN32
1.10 paf 282: // c:\windows
1.36 paf 283: static char root_auto_path[MAX_STRING];
1.30 paf 284: GetWindowsDirectory(root_auto_path, MAX_STRING);
1.3 paf 285: #else
1.40 paf 286: // ~nobody todo: figure out a better place
1.64 paf 287: const char *root_auto_path=SAPI::get_env(pool, "HOME");
1.5 paf 288: #endif
289:
290: // beside by binary
1.36 paf 291: static char site_auto_path[MAX_STRING];
1.98 parser 292: strncpy(site_auto_path, argv0, MAX_STRING-1); site_auto_path[MAX_STRING-1]=0; // filespec of my binary
1.88 parser 293: if(!(
294: rsplit(site_auto_path, '/') ||
1.89 parser 295: rsplit(site_auto_path, '\\'))) { // strip filename
1.88 parser 296: // no path, just filename
297: site_auto_path[0]='.'; site_auto_path[1]=0;
298: }
1.5 paf 299:
300: // process the request
1.35 paf 301: request.core(
1.30 paf 302: root_auto_path, false,
1.31 paf 303: site_auto_path, false,
1.35 paf 304: header_only);
1.57 paf 305:
306: //
307: done_socks();
1.84 parser 308:
309: #ifdef DEBUG_POOL_MALLOC
1.78 parser 310: extern void log_pool_stats(Pool& pool);
311: log_pool_stats(pool);
1.84 parser 312: #endif
1.16 paf 313:
1.22 paf 314: // must be last in PTRY{}PCATCH
1.40 paf 315: #ifdef WIN32
1.5 paf 316: SetUnhandledExceptionFilter(0);
1.25 paf 317: #endif
1.96 parser 318: //
319: if(!cgi)
1.99 ! parser 320: SAPI::send_body(pool, EOL, strlen(EOL));
1.96 parser 321:
1.16 paf 322: // successful finish
323: return 0;
324: } PCATCH(e) { // global problem
1.43 paf 325: // must be first in PCATCH{}
326: #ifdef WIN32
327: SetUnhandledExceptionFilter(0);
328: #endif
1.44 paf 329: // don't allocate anything on pool here:
330: // possible pool' exception not catch-ed now
331: // and there could be out-of-memory exception
1.43 paf 332:
1.19 paf 333: const char *body=e.comment();
1.44 paf 334: // log it
335: SAPI::log(pool, "exception in request exception handler: %s", body);
336:
337: //
1.19 paf 338: int content_length=strlen(body);
1.5 paf 339:
1.35 paf 340: // prepare header
1.37 paf 341: SAPI::add_header_attribute(pool, "content-type", "text/plain");
1.19 paf 342: char content_length_cstr[MAX_NUMBER];
1.60 paf 343: snprintf(content_length_cstr, MAX_NUMBER, "%u", content_length);
1.37 paf 344: SAPI::add_header_attribute(pool, "content-length", content_length_cstr);
1.35 paf 345:
346: // send header
1.37 paf 347: SAPI::send_header(pool);
1.19 paf 348:
349: // body
1.35 paf 350: if(!header_only)
1.37 paf 351: SAPI::send_body(pool, body, content_length);
1.94 parser 352:
1.96 parser 353: //
354: if(!cgi)
1.99 ! parser 355: SAPI::send_body(pool, EOL, strlen(EOL));
1.96 parser 356:
1.16 paf 357: // unsuccessful finish
358: return 1;
359: }
360: PEND_CATCH
1.1 paf 361: }
E-mail: