Annotation of parser3/src/main/pa_request.C, revision 1.245.2.5
1.54 paf 1: /** @file
1.55 paf 2: Parser: request class main part. @see compile.C and execute.C.
3:
1.245.2.5! paf 4: Copyright (c) 2001-2003 ArtLebedev Group (http://www.artlebedev.com)
1.194 paf 5: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.218 paf 6: */
1.55 paf 7:
1.245.2.5! paf 8: static const char* IDENT_REQUEST_C="$Date: 2003/01/31 12:10:47 $";
1.1 paf 9:
1.69 paf 10: #include "pa_sapi.h"
1.53 paf 11: #include "pa_common.h"
1.1 paf 12: #include "pa_request.h"
1.3 paf 13: #include "pa_wwrapper.h"
14: #include "pa_vclass.h"
1.31 paf 15: #include "pa_globals.h"
1.30 paf 16: #include "pa_vint.h"
1.112 paf 17: #include "pa_vmethod_frame.h"
1.32 paf 18: #include "pa_types.h"
1.78 paf 19: #include "pa_vtable.h"
1.90 paf 20: #include "pa_vfile.h"
1.147 parser 21: #include "pa_dictionary.h"
1.208 paf 22: #include "pa_charset.h"
1.186 paf 23: #include "pa_charsets.h"
1.245.2.4 paf 24: #include "pa_cache_managers.h"
1.159 parser 25:
1.245.2.3 paf 26: // defines
27:
28: #define MAIN_METHOD_NAME "main"
29:
30: // consts
31:
1.245.2.5! paf 32: const char* POST_PROCESS_METHOD_NAME="postprocess";
! 33: const char* UNHANDLED_EXCEPTION_METHOD_NAME="unhandled_exception";
1.207 paf 34:
1.82 paf 35: /// content type of exception response, when no @MAIN:exception handler defined
1.245.2.5! paf 36: const char* UNHANDLED_EXCEPTION_CONTENT_TYPE="text/plain";
1.82 paf 37:
38: /// content type of response when no $MAIN:defaults.content-type defined
1.245.2.5! paf 39: const char* DEFAULT_CONTENT_TYPE="text/html";
! 40: const char* ORIGINS_CONTENT_TYPE="text/plain";
1.82 paf 41:
1.245.2.3 paf 42: // globals
43:
1.245.2.4 paf 44: StringPtr main_method_name(new String(MAIN_METHOD_NAME));
1.245.2.3 paf 45:
1.233 paf 46: // op.C
1.245.2.4 paf 47: VStateless_classPtr VClassMAIN_create();
1.195 paf 48: // op.C
49: VHash& exception2vhash(Pool& pool, const Exception& e);
1.123 paf 50:
1.245.2.5! paf 51: void lock_system_class(HashStringValue::key_type key, HashStringValue::value_type value) {
! 52: if(VStateless_class *value_class=value->get_class())
! 53: value_class->lock();
! 54: }
! 55:
1.157 parser 56: //
1.245.2.4 paf 57: Request::Request(SAPI_Info& asapi_info, Request_info& arequest_info,
58: uchar adefault_lang, bool status_allowed):
59: sapi_info(asapi_info),
60: request_info(arequest_info),
1.157 parser 61: fdefault_lang(adefault_lang), flang(adefault_lang),
1.186 paf 62:
1.245.2.4 paf 63: main_class(VClassMAIN_create()),
64: charsets(pool(), *UTF8_charset, *UTF8_charset, *UTF8_charset) // default charsets
65: {
1.178 paf 66: // maybe expire old caches
1.245.2.4 paf 67: cache_managers.maybe_expire();
1.178 paf 68:
1.157 parser 69: /// directly used
1.233 paf 70: // MAIN class, operators
1.245.2.4 paf 71: classes().put(main_class->name(), main_class);
1.157 parser 72: // classes:
73: // table, file, random, mail, image, ...
1.245.2.4 paf 74: methoded_array.register_directly_used(*this);
1.157 parser 75:
76: /// methodless
77: // env class
1.245.2.4 paf 78: classes().put(StringPtr(new String(ENV_CLASS_NAME)),
79: ValuePtr(new VEnv(asapi_info)));
1.175 paf 80: // status class
1.179 paf 81: if(status_allowed)
1.245.2.4 paf 82: classes().put(StringPtr(new String(STATUS_CLASS_NAME)),
83: ValuePtr(new VStatus));
1.157 parser 84: // request class
1.245.2.4 paf 85: classes().put(StringPtr(new String(REQUEST_CLASS_NAME)),
86: ValuePtr(new VRequest(arequest_info, charsets)));
1.157 parser 87: // cookie class
1.245.2.4 paf 88: classes().put(StringPtr(new String(COOKIE_CLASS_NAME)),
89: ValuePtr(new VCookie));
1.157 parser 90:
91: /// methoded
92: // response class
1.245.2.4 paf 93: {
94: ValuePtr response(new VResponse(arequest_info, charsets));
95: classes().put(response->get_class()->name(), response);
96: }
1.157 parser 97:
98: /// bases used
99: // form class
1.245.2.4 paf 100: {
101: ValuePtr form(new VForm);
102: classes().put(form->get_class()->base_class()->name(), form);
103: }
1.213 paf 104: // mail class
1.245.2.4 paf 105: {
106: ValuePtr mail(new VMail);
107: classes().put(mail->get_class()->base_class()->name(), mail);
108: }
1.157 parser 109: // math class
1.245.2.4 paf 110: {
111: ValuePtr math(new VMail);
112: classes().put(math->get_class()->base_class()->name(), math);
113: }
1.245.2.5! paf 114:
! 115: // prevent system classes from modification [calling add_method]
! 116: // ^process[$string:CLASS]{@method} prohibited from now on...
! 117: classes().foreach(lock_system_class);
1.117 paf 118: }
1.192 paf 119:
120: Request::~Request() {
121: #ifdef XML
122: // if for some strange reason xml generic errors failed to be reported, free them up
1.245.2.5! paf 123: if(const char* xml_generic_errors=xmlGenericErrors()) {
1.192 paf 124: SAPI::log(pool(), "warning: unreported xmlGenericErrors: %s", xml_generic_errors);
125: free((void *)xml_generic_errors);
126: }
127: #endif
128: }
1.236 paf 129:
130: Value *Request::get_self() { return method_frame?&method_frame->self():0; }
1.192 paf 131:
1.208 paf 132: static void load_charset(const Hash::Key& akey, Hash::Val *avalue, void *) {
133: charsets->load_charset(akey, static_cast<Value *>(avalue)->as_string());
134: }
135: void Request::configure_admin(VStateless_class& conf_class, const String *source) {
136: if(configure_admin_done)
137: throw Exception("parser.runtime",
138: source,
139: "parser already configured");
140: configure_admin_done=true;
141:
1.227 paf 142: // charsets must only be specified in method_frame config
1.208 paf 143: // so that users would not interfere
144:
145: /* $MAIN:CHARSETS[
146: $.charsetname1[/full/path/to/charset/file.cfg]
147: ...
148: ]
149: */
1.237 paf 150: if(Value *vcharsets=conf_class.get_element(*charsets_name, conf_class, false)) {
1.240 paf 151: if(!vcharsets->is_string())
152: if(Hash *charsets=vcharsets->get_hash(0))
153: charsets->for_each(load_charset);
154: else
155: throw Exception("parser.runtime",
156: 0,
157: "$" MAIN_CLASS_NAME ":" CHARSETS_NAME " must be hash");
1.208 paf 158: }
159:
1.227 paf 160: // configure method_frame options
1.208 paf 161: // until someone with less privileges have overriden them
162: methoded_array->configure_admin(*this);
163: }
1.150 parser 164:
1.64 paf 165: /**
166: load MAIN class, execute @main.
167: MAIN class consists of all the auto.p files we'd manage to find
168: plus
169: the file user requested us to process
170: all located classes become children of one another,
171: composing class we name 'MAIN'
1.180 paf 172:
173: @test log stack trace
174:
1.64 paf 175: */
1.153 parser 176: void Request::core(
1.245.2.5! paf 177: const char* config_filespec, bool config_fail_on_read_problem,
1.62 paf 178: bool header_only) {
1.183 paf 179:
180: #ifdef RESOURCES_DEBUG
181: //measures
182: struct timeval mt[10];
183: //measure:before all
184: gettimeofday(&mt[0],NULL);
185: #endif
1.169 parser 186: try {
1.29 paf 187: char *auto_filespec=(char *)malloc(MAX_STRING);
188:
1.211 paf 189: // loading config
190: if(config_filespec) {
1.77 paf 191: String& filespec=*NEW String(pool());
1.211 paf 192: filespec.APPEND_CLEAN(config_filespec, 0, "config", 0);
1.233 paf 193: use_file(main_class,
194: filespec, true/* ignore class_path */,
195: config_fail_on_read_problem, true/* file must exist if 'fail on read problem' not set */);
1.29 paf 196: }
1.8 paf 197:
1.72 paf 198: // loading auto.p files from document_root/..
199: // to the one beside requested file.
200: // all assigned bases from upper dir
201: {
1.245.2.5! paf 202: const char* after=info.path_translated;
1.115 paf 203: size_t drlen=strlen(info.document_root);
204: if(memcmp(after, info.document_root, drlen)==0) {
205: after+=drlen;
206: if(after[-1]=='/')
207: --after;
208: }
209:
1.152 parser 210: int step=0;
1.245.2.5! paf 211: while(const char* before=strchr(after, '/')) {
1.152 parser 212: String& sfile_spec=*NEW String(pool());
1.115 paf 213: if(after!=info.path_translated) {
1.152 parser 214: sfile_spec.APPEND_CLEAN(
215: info.path_translated, before-info.path_translated,
216: "path-translated-scanned", step++);
217: sfile_spec << "/" AUTO_FILE_NAME;
218:
1.233 paf 219: use_file(main_class,
220: sfile_spec, true/* ignore class_path */,
221: true/* fail on read problem */, false/* but ignore absence, sole user */);
1.115 paf 222: }
223: after=before+1;
1.72 paf 224: }
225: }
1.34 paf 226:
1.125 paf 227: // compile requested file
1.77 paf 228: String& spath_translated=*NEW String(pool());
1.136 parser 229: spath_translated.APPEND_TAINTED(info.path_translated, 0, "user-request", 0);
1.233 paf 230: use_file(main_class,
231: spath_translated, true/* ignore class_path */,
232: true/* fail on read problem*/, true/* fail on abscence */);
1.214 paf 233:
1.227 paf 234: // configure method_frame options if not configured yet
1.214 paf 235: if(!configure_admin_done)
1.233 paf 236: configure_admin(main_class, 0);
1.7 paf 237:
1.227 paf 238: // configure not-method_frame=user options
1.124 paf 239: methoded_array->configure_user(*this);
240:
1.85 paf 241: // $MAIN:MIME-TYPES
1.237 paf 242: if(Value *element=main_class.get_element(*mime_types_name, main_class, false))
1.85 paf 243: if(Table *table=element->get_table())
244: mime_types=table;
1.102 paf 245:
1.124 paf 246: // filling form fields
1.153 parser 247: form.fill_fields_and_tables(*this);
1.102 paf 248:
1.124 paf 249: // filling cookies
250: cookie.fill_fields(*this);
1.213 paf 251:
252: // filling mail received
253: mail.fill_received(*this);
1.25 paf 254:
1.183 paf 255: #ifdef RESOURCES_DEBUG
256: //measure:after compile
257: gettimeofday(&mt[1],NULL);
258: #endif
1.25 paf 259: // execute @main[]
1.233 paf 260: const String *body_string=execute_virtual_method(main_class, main_method_name);
1.41 paf 261: if(!body_string)
1.197 paf 262: throw Exception("parser.runtime",
1.145 parser 263: 0,
264: "'"MAIN_METHOD_NAME"' method not found");
1.79 paf 265:
1.183 paf 266: #ifdef RESOURCES_DEBUG
267: //measure:after main
268: gettimeofday(&mt[2],NULL);
269: #endif
270:
1.91 paf 271: VString body_vstring_before_post_process(*body_string);
272: VString *body_vstring_after_post_process=&body_vstring_before_post_process;
1.119 paf 273: // @postprocess
1.233 paf 274: if(Value *value=main_class.get_element(
1.245.2.4 paf 275: StringPtr(new String(POST_PROCESS_METHOD_NAME),
1.237 paf 276: main_class,
1.226 paf 277: false))
1.91 paf 278: if(Junction *junction=value->get_junction())
279: if(const Method *method=junction->method) {
280: // preparing to pass parameters to
1.119 paf 281: // @postprocess[data]
1.234 paf 282: VMethodFrame frame(pool(), method->name, *junction, 0/*no parent*/);
1.233 paf 283: frame.set_self(main_class);
1.91 paf 284:
1.202 paf 285: frame.store_param(&body_vstring_before_post_process);
1.91 paf 286: body_vstring_after_post_process=
1.188 paf 287: NEW VString(execute_method(frame, *method));
1.91 paf 288: }
1.90 paf 289:
1.204 paf 290: bool lorigins_mode=origins_mode();
1.144 parser 291:
292: const VFile *body_file=body_vstring_after_post_process->as_vfile(
1.204 paf 293: String::UL_UNSPECIFIED, lorigins_mode);
1.41 paf 294:
1.45 paf 295: // extract response body
296: Value *body_value=static_cast<Value *>(
1.244 paf 297: response.fields().get(*download_name));
298: bool as_attachment=body_value!=0;
299: if(!body_value)
300: body_value=static_cast<Value *>(
301: response.fields().get(*body_name));
302:
1.216 paf 303: if(body_value) // there is some $response:body
1.90 paf 304: body_file=body_value->as_vfile();
1.204 paf 305: else if(lorigins_mode)
1.144 parser 306: response.fields().put(*content_type_name,
1.245.2.4 paf 307: NEW VString(StringPtr(new String(ORIGINS_CONTENT_TYPE)));
1.45 paf 308:
1.183 paf 309: #ifdef RESOURCES_DEBUG
310: //measure:after postprocess
311: gettimeofday(&mt[3],NULL);
312: #endif
313:
1.45 paf 314: // OK. write out the result
1.244 paf 315: output_result(*body_file, header_only, as_attachment);
1.183 paf 316:
317: #ifdef RESOURCES_DEBUG
318: //measure:after output_result
319: gettimeofday(&mt[9],NULL);
320: t[9]=mt[9].tv_sec+mt[9].tv_usec/1000000.0;
321:
322: double t[10];
323: for(int i=0;i<10;i++)
324: t[i]=mt[i].tv_sec+mt[i].tv_usec/1000000.0;
325: //measure:log2 compile,main,postprocess,output
326: SAPI::log(pool(), "rmeasure: %s,%.2f,%.2f,%.2f %.2f,%.2f %.2f",
327: info.uri,
328: t[1]-t[0],
329: t[2]-t[1],
330: t[3]-t[2],
331: sql_connect_time,sql_request_time,
332: t[9]-t[3]
333: );
334: #endif
1.171 parser 335: } catch(const Exception& e) { // request handling problem
1.76 paf 336: // we're returning not result, but error explanation
1.169 parser 337: try {
1.76 paf 338: // log the beast
1.205 paf 339: if(const String *problem_source=e.problem_source())
1.76 paf 340: SAPI::log(pool(),
1.199 paf 341: "%s: "
1.76 paf 342: #ifndef NO_STRING_ORIGIN
1.180 paf 343: ORIGIN_FILE_LINE_FORMAT": "
1.76 paf 344: #endif
1.197 paf 345: "'%s' %s [%s]",
1.199 paf 346: info.uri,
1.76 paf 347: #ifndef NO_STRING_ORIGIN
348: problem_source->origin().file?problem_source->origin().file:"global",
349: problem_source->origin().line,
350: #endif
1.173 paf 351: problem_source->cstr(),
1.76 paf 352: e.comment(),
1.206 paf 353: e.type()
1.76 paf 354: );
355: else
356: SAPI::log(pool(),
1.199 paf 357: "%s: "
1.197 paf 358: "%s [%s]",
1.199 paf 359: info.uri,
1.206 paf 360: e.comment(), e.type()
1.180 paf 361: );
1.171 parser 362:
1.43 paf 363: // reset language to default
364: flang=fdefault_lang;
365:
366: // reset response
367: response.fields().clear();
368:
369: // this is what we'd return in $response:body
1.41 paf 370: const String *body_string=0;
1.43 paf 371:
1.233 paf 372: // maybe we'd be lucky enough as to report an error
373: // in a gracefull way...
374: if(Value *value=main_class.get_element(
1.245.2.4 paf 375: StringPtr(new String(UNHANDLED_EXCEPTION_METHOD_NAME),
1.237 paf 376: main_class,
1.233 paf 377: false)) {
378: if(Junction *junction=value->get_junction()) {
379: if(const Method *method=junction->method) {
380: // preparing to pass parameters to
381: // @unhandled_exception[exception;stack]
1.234 paf 382: VMethodFrame frame(pool(), method->name, *junction, 0/*no caller*/);
1.233 paf 383: frame.set_self(main_class);
384:
385: // $exception
386: frame.store_param(&exception2vhash(pool(), e));
387: // $stack[^table::set{name origin}]
388: Array& stack_trace_columns=*NEW Array(pool());
389: stack_trace_columns+=NEW String(pool(), "name");
390: stack_trace_columns+=NEW String(pool(), "file");
391: stack_trace_columns+=NEW String(pool(), "lineno");
392: Table& stack_trace=*NEW Table(pool(), 0, &stack_trace_columns);
393: Array_iter tracei(exception_trace);
394: while(tracei.has_next()) {
395: Array& row=*NEW Array(pool());
1.171 parser 396:
1.233 paf 397: const String *name=(const String *)tracei.next();
398: row+=name; // name column
1.171 parser 399: #ifndef NO_STRING_ORIGIN
1.233 paf 400: const Origin& origin=name->origin();
401: if(origin.file) {
402: row+=NEW String(pool(), origin.file, 0, true); // file column
403: char *buf=(char *)malloc(MAX_NUMBER);
404: size_t buf_size=snprintf(buf, MAX_NUMBER, "%d", 1+origin.line);
405: row+=NEW String(pool(), buf, buf_size, true); // line column
406: }
1.171 parser 407: #endif
1.233 paf 408: stack_trace+=&row;
409: }
410: frame.store_param(NEW VTable(pool(), &stack_trace));
1.171 parser 411:
1.233 paf 412: // future $response:body=
413: // execute ^unhandled_exception[exception;stack]
414: body_string=&execute_method(frame, *method);
415: }
416: }
1.30 paf 417: }
418:
1.41 paf 419: if(!body_string) { // couldn't report an error beautifully?
420: // doing that ugly
421:
422: // make up result: $origin $source $comment $type $code
423: char *buf=(char *)malloc(MAX_STRING);
1.30 paf 424: size_t printed=0;
1.205 paf 425: if(const String *problem_source=e.problem_source()) {
1.16 paf 426: #ifndef NO_STRING_ORIGIN
1.30 paf 427: const Origin& origin=problem_source->origin();
428: if(origin.file)
1.180 paf 429: printed+=snprintf(buf+printed, MAX_STRING-printed,
430: ORIGIN_FILE_LINE_FORMAT": ",
431: origin.file, 1+origin.line
432: );
1.28 paf 433: #endif
1.41 paf 434: printed+=snprintf(buf+printed, MAX_STRING-printed, "'%s' ",
1.173 paf 435: problem_source->cstr());
1.30 paf 436: }
1.245.2.5! paf 437: if(const char* comment=e.comment(true))
1.206 paf 438: printed+=snprintf(buf+printed, MAX_STRING-printed, "%s", comment);
1.245.2.5! paf 439: if(const char* type=e.type(true))
1.197 paf 440: printed+=snprintf(buf+printed, MAX_STRING-printed, " type: %s", type);
1.43 paf 441:
442: // future $response:content-type
1.49 paf 443: response.fields().put(*content_type_name,
1.245.2.4 paf 444: NEW VString(StringPtr(new String(UNHANDLED_EXCEPTION_CONTENT_TYPE)));
1.43 paf 445: // future $response:body
1.44 paf 446: body_string=NEW String(pool(), buf);
1.30 paf 447: }
1.41 paf 448:
1.144 parser 449: VString body_vstring(*body_string);
1.90 paf 450: const VFile *body_file=body_vstring.as_vfile();
451:
1.45 paf 452: // ERROR. write it out
1.244 paf 453: output_result(*body_file, header_only, false);
1.170 parser 454: } catch(const Exception& ) {
1.169 parser 455: /*re*/throw;
1.3 paf 456: }
1.1 paf 457: }
458: }
459:
1.245.2.3 paf 460: VStateless_classPtr Request::use_file(VStateless_class& aclass,
1.245.2.4 paf 461: StringPtr file_name, bool ignore_class_path,
1.233 paf 462: bool fail_on_read_problem, bool fail_on_file_absence) {
1.73 paf 463: // cyclic dependence check
1.148 parser 464: if(used_files.get(file_name))
1.233 paf 465: return 0;
1.245.2.2 paf 466: used_files.put(file_name, true);
1.73 paf 467:
1.148 parser 468: const String *file_spec;
1.153 parser 469: if(ignore_class_path) // ignore_class_path?
1.148 parser 470: file_spec=&file_name;
1.153 parser 471: else if(file_name.first_char()=='/') //absolute path, no need to scan MAIN:CLASS_PATH?
472: file_spec=&absolute(file_name);
473: else {
474: file_spec=0;
1.237 paf 475: if(Value *element=main_class.get_element(*class_path_name, main_class, false)) {
1.233 paf 476: if(element->is_string()) {
477: file_spec=file_readable(absolute(element->as_string()), file_name); // found at class_path?
478: } else if(Table *table=element->get_table()) {
479: int size=table->size();
480: for(int i=size; i--; ) {
481: const String& path=*static_cast<Array *>(table->get(i))->get_string(0);
482: if(file_spec=file_readable(absolute(path), file_name))
483: break; // found along class_path
484: }
485: } else
486: throw Exception("parser.runtime",
487: 0,
488: "$" CLASS_PATH_NAME " must be string or table");
489: if(!file_spec)
490: throw Exception("parser.runtime",
491: &file_name,
492: "not found along " MAIN_CLASS_NAME ":" CLASS_PATH_NAME);
493: }
1.153 parser 494: if(!file_spec)
1.197 paf 495: throw Exception("parser.runtime",
1.153 parser 496: &file_name,
1.203 paf 497: "usage failed - no $" MAIN_CLASS_NAME ":" CLASS_PATH_NAME " were specified");
1.148 parser 498: }
1.230 paf 499:
500: if(fail_on_read_problem && !fail_on_file_absence) // ignore file absence if asked for
501: if(!entry_exists(*file_spec))
1.233 paf 502: return 0;
1.73 paf 503:
1.148 parser 504: char *source=file_read_text(pool(), *file_spec, fail_on_read_problem);
1.3 paf 505: if(!source)
1.233 paf 506: return 0;
1.3 paf 507:
1.233 paf 508: return use_buf(aclass, source, *file_spec, file_spec->cstr());
1.16 paf 509: }
510:
1.208 paf 511:
1.245.2.3 paf 512: VStateless_classPtr Request::use_buf(VStateless_class& aclass,
1.245.2.5! paf 513: const char* source,
! 514: const String& filespec, const char* filespec_cstr) {
1.233 paf 515: // temporary zero @conf so to maybe-replace it in compiled code
516: Temp_method temp_method_conf(aclass, *conf_method_name, 0);
517: // temporary zero @auto so to maybe-replace it in compiled code
518: Temp_method temp_method_auto(aclass, *auto_method_name, 0);
519:
1.5 paf 520: // compile loaded class
1.233 paf 521: VStateless_class& cclass=COMPILE(aclass, source, filespec_cstr);
1.212 paf 522:
1.233 paf 523: // locate and execute possible @conf[] static
1.212 paf 524: VString *vfilespec=NEW VString(filespec);
1.209 paf 525: const Method *method_called;
1.212 paf 526: execute_nonvirtual_method(cclass,
1.233 paf 527: *conf_method_name, vfilespec,
1.212 paf 528: 0/*no result needed*/, &method_called);
1.233 paf 529: if(method_called)
1.209 paf 530: configure_admin(cclass, &method_called->name);
1.208 paf 531:
532: // locate and execute possible @auto[] static
1.233 paf 533: execute_nonvirtual_method(cclass,
534: *auto_method_name, vfilespec,
1.212 paf 535: 0/*no result needed*/);
1.16 paf 536: return &cclass;
1.19 paf 537: }
538:
1.245.2.5! paf 539: const String& Request::relative(const char* apath, const String& relative_name) {
1.217 paf 540: int hpath_buf_size=strlen(apath)+1;
541: char *hpath=(char *)malloc(hpath_buf_size);
542: memcpy(hpath, apath, hpath_buf_size);
543: String& result=*NEW String(pool());
544: if(rsplit(hpath, '/')) // if something/splitted
545: result << hpath << "/";
546: result << relative_name;
1.19 paf 547: return result;
548: }
549:
1.77 paf 550: const String& Request::absolute(const String& relative_name) {
1.217 paf 551: if(relative_name.first_char()=='/') {
1.245.2.4 paf 552: String& result=StringPtr(new String(info.document_root);
1.104 paf 553: result << relative_name;
1.19 paf 554: return result;
555: } else
1.239 paf 556: return relative_name.pos("://")<0? relative(info.path_translated, relative_name)
557: :relative_name; // something like "http://xxx"
1.1 paf 558: }
1.45 paf 559:
1.101 paf 560: static void add_header_attribute(const Hash::Key& aattribute, Hash::Val *ameaning,
561: void *info) {
1.178 paf 562: Request& r=*static_cast<Request *>(info);
1.184 paf 563: if(aattribute==BODY_NAME
1.244 paf 564: || aattribute==DOWNLOAD_NAME
1.184 paf 565: || aattribute==CHARSET_NAME)
1.101 paf 566: return;
567:
568: Value& lmeaning=*static_cast<Value *>(ameaning);
569: Pool& pool=lmeaning.pool();
570:
571: SAPI::add_header_attribute(pool,
1.173 paf 572: aattribute.cstr(),
1.243 paf 573: attributed_meaning_to_string(lmeaning, String::UL_HTTP_HEADER, false)
574: .cstr(String::UL_UNSPECIFIED));
1.101 paf 575: }
1.244 paf 576: void Request::output_result(const VFile& body_file, bool header_only, bool as_attachment) {
1.51 paf 577: // header: cookies
578: cookie.output_result();
579:
1.223 paf 580: Value *body_file_content_type;
1.90 paf 581: // set content-type
1.223 paf 582: if(body_file_content_type=static_cast<Value *>(
1.222 paf 583: body_file.fields().get(*content_type_name))) {
1.90 paf 584: // body file content type
585: response.fields().put(*content_type_name, body_file_content_type);
586: } else {
587: // default content type
588: response.fields().put_dont_replace(*content_type_name,
589: default_content_type?default_content_type
1.245.2.4 paf 590: :NEW VString(StringPtr(new String(DEFAULT_CONTENT_TYPE)));
1.92 paf 591: }
592:
593: // content-disposition
1.111 paf 594: if(VString *vfile_name=static_cast<VString *>(body_file.fields().get(*name_name)))
595: if(vfile_name->string()!=NONAME_DAT) {
596: VHash& vhash=*NEW VHash(pool());
1.242 paf 597: Hash& hash=vhash.hash(0);
1.244 paf 598: if(as_attachment)
599: hash.put(*value_name, NEW VString(*content_disposition_value));
1.242 paf 600: hash.put(*content_disposition_filename_name, vfile_name);
1.111 paf 601: response.fields().put(*content_disposition_name, &vhash);
602: }
1.49 paf 603:
1.62 paf 604: // prepare header: $response:fields without :body
1.178 paf 605: response.fields().for_each(add_header_attribute, this);
1.50 paf 606:
1.184 paf 607: const void *client_body;
608: size_t client_content_length;
1.220 paf 609: // transcode text body when "text/*" or simple result
1.223 paf 610: if(body_file_content_type)
611: if(Hash *hash=body_file_content_type->get_hash(0))
612: body_file_content_type=static_cast<Value *>(hash->get(*value_name));
1.220 paf 613: if(!body_file_content_type/*vstring.as_vfile*/ || body_file_content_type->as_string().pos("text/")==0) {
614: Charset::transcode(pool(),
615: pool().get_source_charset(), body_file.value_ptr(), body_file.value_size(),
616: pool().get_client_charset(), client_body, client_content_length
617: );
618: } else {
619: client_body=body_file.value_ptr();
620: client_content_length=body_file.value_size();
621: }
1.50 paf 622:
1.62 paf 623: // prepare header: content-length
1.200 paf 624: char content_length_cstr[MAX_NUMBER];
625: snprintf(content_length_cstr, MAX_NUMBER, "%u", client_content_length);
626: SAPI::add_header_attribute(pool(), "content-length", content_length_cstr);
1.62 paf 627:
628: // send header
1.69 paf 629: SAPI::send_header(pool());
1.71 paf 630:
1.62 paf 631: // send body
632: if(!header_only)
1.184 paf 633: SAPI::send_body(pool(), client_body, client_content_length);
1.50 paf 634: }
1.104 paf 635:
1.245.2.5! paf 636: const String& Request::mime_type_of(const char* user_file_name_cstr) {
1.104 paf 637: if(mime_types)
1.245.2.5! paf 638: if(const char* cext=strrchr(user_file_name_cstr, '.')) {
1.104 paf 639: String sext(pool(), ++cext);
1.221 paf 640: if(mime_types->locate(0, sext.change_case(pool(), String::CC_LOWER)))
1.104 paf 641: if(const String *result=mime_types->item(1))
642: return *result;
643: else
1.197 paf 644: throw Exception("parser.runtime",
1.104 paf 645: mime_types->origin_string(),
1.212 paf 646: MIME_TYPES_NAME " table column elements must not be empty");
1.104 paf 647: }
1.245.2.4 paf 648: return StringPtr(new String("application/octet-stream");
1.233 paf 649: }
650:
651: bool Request::origins_mode() {
1.237 paf 652: return main_class.get_element(*origins_mode_name, main_class, false)!=0; // $ORIGINS mode
1.133 parser 653: }
E-mail: