|
|
| version 1.414, 2021/01/02 10:49:22 | version 1.439, 2026/04/25 13:38:46 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: request class main part. @see compile.C and execute.C. | Parser: request class main part. @see compile.C and execute.C. |
| Copyright (c) 2001-2020 Art. Lebedev Studio (http://www.artlebedev.com) | Copyright (c) 2001-2026 Art. Lebedev Studio (https://www.artlebedev.com) |
| Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru) | Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru> |
| */ | */ |
| #include "pa_sapi.h" | #include "pa_sapi.h" |
| Line 33 | Line 33 |
| #include "pa_vmemory.h" | #include "pa_vmemory.h" |
| #include "pa_vconsole.h" | #include "pa_vconsole.h" |
| #include "pa_vdate.h" | #include "pa_vdate.h" |
| #include "pa_varray.h" | |
| volatile const char * IDENT_PA_REQUEST_C="$Id$" IDENT_PA_REQUEST_H IDENT_PA_REQUEST_CHARSETS_H IDENT_PA_REQUEST_INFO_H IDENT_PA_VCONSOLE_H; | volatile const char * IDENT_PA_REQUEST_C="$Id$" IDENT_PA_REQUEST_H IDENT_PA_REQUEST_CHARSETS_H IDENT_PA_REQUEST_INFO_H IDENT_PA_VCONSOLE_H; |
| Line 43 volatile const char * IDENT_PA_REQUEST_C | Line 44 volatile const char * IDENT_PA_REQUEST_C |
| /// content type of response when no $MAIN:defaults.content-type defined | /// content type of response when no $MAIN:defaults.content-type defined |
| const char* DEFAULT_CONTENT_TYPE="text/html"; | const char* DEFAULT_CONTENT_TYPE="text/html"; |
| const uint LOOP_LIMIT=20000; | const uint LOOP_LIMIT=100000; |
| const uint EXECUTE_RECOURSION_LIMIT=1000; | const uint ARRAY_LIMIT=1000000; |
| const uint EXECUTE_RECURSION_LIMIT=1000; | |
| const uint HTTPD_TIMEOUT=4; | const uint HTTPD_TIMEOUT=4; |
| const size_t FILE_SIZE_LIMIT=512*1024*1024; | const size_t FILE_SIZE_LIMIT=512*1024*1024; |
| Line 68 const String auto_method_name(AUTO_METHO | Line 70 const String auto_method_name(AUTO_METHO |
| static const String use_method_name(USE_METHOD_NAME); | static const String use_method_name(USE_METHOD_NAME); |
| static const String autouse_method_name(AUTOUSE_METHOD_NAME); | static const String autouse_method_name(AUTOUSE_METHOD_NAME); |
| const String exception_type_part_name(EXCEPTION_TYPE_PART_NAME); | const String::Body exception_type_part_name(EXCEPTION_TYPE_PART_NAME); |
| const String exception_source_part_name(EXCEPTION_SOURCE_PART_NAME); | const String::Body exception_source_part_name(EXCEPTION_SOURCE_PART_NAME); |
| const String exception_comment_part_name(EXCEPTION_COMMENT_PART_NAME); | const String::Body exception_comment_part_name(EXCEPTION_COMMENT_PART_NAME); |
| const String exception_handled_part_name(EXCEPTION_HANDLED_PART_NAME); | const String::Body exception_handled_part_name(EXCEPTION_HANDLED_PART_NAME); |
| static const String origin_key(ORIGIN_KEY); | static const String origin_key(ORIGIN_KEY); |
| int pa_loop_limit=LOOP_LIMIT; | int pa_loop_limit=LOOP_LIMIT; |
| int pa_execute_recoursion_limit=EXECUTE_RECOURSION_LIMIT; | int pa_array_limit=ARRAY_LIMIT; |
| int pa_execute_recursion_limit=EXECUTE_RECURSION_LIMIT; | |
| int pa_httpd_timeout=HTTPD_TIMEOUT; | int pa_httpd_timeout=HTTPD_TIMEOUT; |
| size_t pa_file_size_limit=FILE_SIZE_LIMIT; | size_t pa_file_size_limit=FILE_SIZE_LIMIT; |
| Line 97 static const String class_path_name(CLAS | Line 100 static const String class_path_name(CLAS |
| static const String charsets_name("CHARSETS"); | static const String charsets_name("CHARSETS"); |
| static const String strict_vars_name("STRICT-VARS"); | static const String strict_vars_name("STRICT-VARS"); |
| static const String prototype_name("OBJECT-PROTOTYPE"); | static const String prototype_name("OBJECT-PROTOTYPE"); |
| static const String getter_protected_name("CLASS-GETTER-PROTECTED"); | |
| static const String locals_name("LOCALS"); | |
| static const String limits_name("LIMITS"); | static const String limits_name("LIMITS"); |
| static const String loop_limit_name("max_loop"); | static const String loop_limit_name("max_loop"); |
| static const String recoursion_limit_name("max_recoursion"); | static const String array_limit_name("max_array_size"); |
| static const String recursion_limit_name("max_recursion"); | |
| static const String file_size_limit_name("max_file_size"); | static const String file_size_limit_name("max_file_size"); |
| static const String lock_wait_timeout_name("lock_wait_timeout"); | static const String lock_wait_timeout_name("lock_wait_timeout"); |
| static const String httpd_name("HTTPD"); | static const String httpd_name("HTTPD"); |
| Line 120 static const String content_disposition_ | Line 126 static const String content_disposition_ |
| static const String content_disposition_inline(CONTENT_DISPOSITION_INLINE); | static const String content_disposition_inline(CONTENT_DISPOSITION_INLINE); |
| static const String content_disposition_attachment(CONTENT_DISPOSITION_ATTACHMENT); | static const String content_disposition_attachment(CONTENT_DISPOSITION_ATTACHMENT); |
| const String content_disposition_filename_name(CONTENT_DISPOSITION_FILENAME_NAME); | |
| const String content_disposition_filename_name_asterisk("filename*"); | |
| // defines | // defines |
| #define CHARSET_NAME_UPPER "CHARSET" | #define CHARSET_NAME_UPPER "CHARSET" |
| Line 131 VStateless_class& VClassMAIN_create(); | Line 140 VStateless_class& VClassMAIN_create(); |
| // | // |
| Request::Request(SAPI_Info& asapi_info, Request_info& arequest_info, String::Language adefault_lang): | Request::Request(SAPI_Info& asapi_info, Request_info& arequest_info, String::Language adefault_lang): |
| // private | // private |
| anti_endless_execute_recoursion(0), | anti_endless_execute_recursion(0), |
| // public | // public |
| allow_class_replace(false), | allow_class_replace(false), |
| Line 166 Request::Request(SAPI_Info& asapi_info, | Line 175 Request::Request(SAPI_Info& asapi_info, |
| pa_register_thread_request(*this); | pa_register_thread_request(*this); |
| // file_no=0 => unknown | // file_no=0 => unknown |
| file_list+="UNKNOWN"; | file_list+=String::Body("UNKNOWN"); |
| file_list+="-body of process-"; // pseudo_file_no__process | file_list+=String::Body("-body of process-"); // pseudo_file_no__process |
| // maybe expire old caches | // maybe expire old caches |
| cache_managers->maybe_expire(); | cache_managers->maybe_expire(); |
| Line 240 VStateless_class& Request::get_class_ref | Line 249 VStateless_class& Request::get_class_ref |
| return *result; | return *result; |
| } | } |
| bool Request::add_class(const char* atype, VStateless_class *aclass){ | |
| if(!classes().put_dont_replace(String::Body(atype), aclass)) | |
| return true; | |
| if(!allow_class_replace && strcmp(atype, VARRAY_TYPE)) | |
| return false; | |
| classes().remove(String::Body(atype)); | |
| classes().put(atype, aclass); | |
| return true; | |
| } | |
| static void load_charset(HashStringValue::key_type akey, HashStringValue::value_type avalue, Request_charsets* charsets) { | static void load_charset(HashStringValue::key_type akey, HashStringValue::value_type avalue, Request_charsets* charsets) { |
| pa_charsets.load_charset(*charsets, akey, avalue->as_string()); | pa_charsets.load_charset(*charsets, akey, avalue->as_string()); |
| } | } |
| Line 251 static void load_charset(HashStringValue | Line 270 static void load_charset(HashStringValue |
| if(option->is_evaluated_expr()) { \ | if(option->is_evaluated_expr()) { \ |
| code; \ | code; \ |
| } else \ | } else \ |
| throw Exception(PARSER_RUNTIME, 0, "$main:" exception_name, name.cstr()); \ | throw Exception(PARSER_RUNTIME, 0, "$MAIN:" exception_name, name.cstr()); \ |
| } | } |
| void Request::configure_admin(VStateless_class& conf_class) { | void Request::configure_admin(VStateless_class& conf_class) { |
| Line 272 void Request::configure_admin(VStateless | Line 291 void Request::configure_admin(VStateless |
| if(HashStringValue* charsets=vcharsets->get_hash()) | if(HashStringValue* charsets=vcharsets->get_hash()) |
| charsets->for_each<Request_charsets*>(load_charset, &this->charsets); | charsets->for_each<Request_charsets*>(load_charset, &this->charsets); |
| else | else |
| throw Exception(PARSER_RUNTIME, 0, "$main:%s must be hash", charsets_name.cstr()); | throw Exception(PARSER_RUNTIME, 0, "$MAIN:%s must be hash", charsets_name.cstr()); |
| } | } |
| } | } |
| Line 282 void Request::configure_admin(VStateless | Line 301 void Request::configure_admin(VStateless |
| if(strict_vars->is_bool()) | if(strict_vars->is_bool()) |
| VVoid::strict_vars=strict_vars->as_bool(); | VVoid::strict_vars=strict_vars->as_bool(); |
| else | else |
| throw Exception(PARSER_RUNTIME, 0, "$main:%s must be bool", strict_vars_name.cstr()); | throw Exception(PARSER_RUNTIME, 0, "$MAIN:%s must be bool", strict_vars_name.cstr()); |
| } | } |
| #endif | #endif |
| Line 292 void Request::configure_admin(VStateless | Line 311 void Request::configure_admin(VStateless |
| if(prototype->is_bool()) | if(prototype->is_bool()) |
| VClass::prototype=prototype->as_bool(); | VClass::prototype=prototype->as_bool(); |
| else | else |
| throw Exception(PARSER_RUNTIME, 0, "$main:%s must be bool", prototype_name.cstr()); | throw Exception(PARSER_RUNTIME, 0, "$MAIN:%s must be bool", prototype_name.cstr()); |
| } | |
| #endif | |
| #ifdef CLASS_GETTER_UNPROTECTED | |
| VClass::getter_protected=true; | |
| if(Value* getter_protected=conf_class.get_element(getter_protected_name)) { | |
| if(getter_protected->is_bool()) | |
| VClass::getter_protected=getter_protected->as_bool(); | |
| else | |
| throw Exception(PARSER_RUNTIME, 0, "$MAIN:%s must be bool", getter_protected_name.cstr()); | |
| } | } |
| #endif | #endif |
| VStateless_class::gall_vars_local=false; | |
| if(Value* locals=conf_class.get_element(locals_name)) { | |
| if(locals->is_bool()){ | |
| VStateless_class::gall_vars_local=locals->as_bool(); | |
| main_class.set_all_vars_local(); | |
| } else | |
| throw Exception(PARSER_RUNTIME, 0, "$MAIN:%s must be bool", locals_name.cstr()); | |
| } | |
| Value* limits=conf_class.get_element(limits_name); | Value* limits=conf_class.get_element(limits_name); |
| pa_loop_limit=LOOP_LIMIT; | pa_loop_limit=LOOP_LIMIT; |
| Line 304 void Request::configure_admin(VStateless | Line 342 void Request::configure_admin(VStateless |
| if(pa_loop_limit==0) pa_loop_limit=INT_MAX; | if(pa_loop_limit==0) pa_loop_limit=INT_MAX; |
| }, "LIMITS.%s must be int"); | }, "LIMITS.%s must be int"); |
| pa_execute_recoursion_limit=EXECUTE_RECOURSION_LIMIT; | pa_array_limit=ARRAY_LIMIT; |
| CONF_OPTION(limits, recoursion_limit_name, { | CONF_OPTION(limits, array_limit_name, { |
| pa_execute_recoursion_limit=option->as_int(); | pa_array_limit=option->as_int(); |
| if(pa_execute_recoursion_limit==0) pa_execute_recoursion_limit=INT_MAX; | if(pa_array_limit==0) pa_array_limit=INT_MAX; |
| }, "LIMITS.%s must be int"); | |
| pa_execute_recursion_limit=EXECUTE_RECURSION_LIMIT; | |
| CONF_OPTION(limits, recursion_limit_name, { | |
| pa_execute_recursion_limit=option->as_int(); | |
| if(pa_execute_recursion_limit==0) pa_execute_recursion_limit=INT_MAX; | |
| }, "LIMITS.%s must be int"); | }, "LIMITS.%s must be int"); |
| pa_file_size_limit=FILE_SIZE_LIMIT; | pa_file_size_limit=FILE_SIZE_LIMIT; |
| CONF_OPTION(limits, file_size_limit_name, { | CONF_OPTION(limits, file_size_limit_name, { |
| double limit=option->as_double(); | double limit=option->as_double(); |
| if(limit >= (double)SSIZE_MAX) | if(limit >= (double)SSIZE_MAX) |
| throw Exception(PARSER_RUNTIME, 0, "$main:LIMITS.%s must be less then %.15g", file_size_limit_name.cstr(), (double)SSIZE_MAX); | throw Exception(PARSER_RUNTIME, 0, "$MAIN:LIMITS.%s must be less than %.15g", file_size_limit_name.cstr(), (double)SSIZE_MAX); |
| pa_file_size_limit=(size_t)limit; | pa_file_size_limit=(size_t)limit; |
| if(pa_file_size_limit==0) | if(pa_file_size_limit==0) |
| pa_file_size_limit=SSIZE_MAX; | pa_file_size_limit=SSIZE_MAX; |
| Line 324 void Request::configure_admin(VStateless | Line 368 void Request::configure_admin(VStateless |
| CONF_OPTION(limits, lock_wait_timeout_name, { | CONF_OPTION(limits, lock_wait_timeout_name, { |
| double limit=option->as_double(); | double limit=option->as_double(); |
| if(limit >= 3600*24) | if(limit >= 3600*24) |
| throw Exception(PARSER_RUNTIME, 0, "$main:LIMITS.%s must be less then %d", lock_wait_timeout_name.cstr(), 3600*24); | throw Exception(PARSER_RUNTIME, 0, "$MAIN:LIMITS.%s must be less than %d", lock_wait_timeout_name.cstr(), 3600*24); |
| pa_lock_attempts=(unsigned int)(limit*2)+1; | pa_lock_attempts=(unsigned int)(limit*2)+1; |
| }, "LIMITS.%s must be number"); | }, "LIMITS.%s must be number"); |
| Line 339 void Request::configure_admin(VStateless | Line 383 void Request::configure_admin(VStateless |
| if(httpd) | if(httpd) |
| if(Value* option=httpd->get_element( httpd_mode_name)) { | if(Value* option=httpd->get_element( httpd_mode_name)) { |
| if(option->get_junction()) | if(option->get_junction()) |
| throw Exception(PARSER_RUNTIME, 0, "$main:HTTPD:mode must be string"); | throw Exception(PARSER_RUNTIME, 0, "$MAIN:HTTPD:mode must be string"); |
| HTTPD_Server::set_mode(option->as_string()); | HTTPD_Server::set_mode(option->as_string()); |
| } | } |
| Line 404 Table &Request::Exception_trace::table(R | Line 448 Table &Request::Exception_trace::table(R |
| Operation::Origin origin=trace.origin(); | Operation::Origin origin=trace.origin(); |
| if(origin.file_no) { | if(origin.file_no) { |
| *row+=new String(r.file_list[origin.file_no], String::L_TAINTED); // 'file' column | *row+=new String(r.file_list[origin.file_no], String::L_TAINTED); // 'file' column |
| *row+=new String(String::Body::Format(1+origin.line), String::L_CLEAN); // 'lineno' column | *row+=new String(pa_uitoa(1+origin.line), String::L_CLEAN); // 'lineno' column |
| *row+=new String(String::Body::Format(1+origin.col), String::L_CLEAN); // 'colno' column | *row+=new String(pa_uitoa(1+origin.col), String::L_CLEAN); // 'colno' column |
| } | } |
| stack_trace+=row; | stack_trace+=row; |
| } | } |
| Line 418 void Request::configure() { | Line 462 void Request::configure() { |
| if(!configure_admin_done) | if(!configure_admin_done) |
| configure_admin(main_class); | configure_admin(main_class); |
| // configure not-admin=user options | |
| methoded_array().configure_user(*this); | |
| // $MAIN:MIME-TYPES | // $MAIN:MIME-TYPES |
| if(Value* element=main_class.get_element(mime_types_name)) | if(Value* element=main_class.get_element(mime_types_name)) |
| if(Table *table=element->get_table()) | if(Table *table=element->get_table()) |
| Line 439 void Request::configure() { | Line 480 void Request::configure() { |
| */ | */ |
| void Request::core(const char* config_filespec, bool header_only, const String &amain_method_name, const String* amain_class_name) { | void Request::core(const char* config_filespec, bool header_only, const String &amain_method_name, const String* amain_class_name) { |
| VFile* body_file=NULL; | |
| bool as_attachment=false; | |
| try { | try { |
| // loading config | // loading config |
| if(config_filespec) | if(config_filespec) |
| Line 468 void Request::core(const char* config_fi | Line 506 void Request::core(const char* config_fi |
| // extract response body | // extract response body |
| Value* body_value=response.fields().get(download_name_upper); // $response:download? | Value* body_value=response.fields().get(download_name_upper); // $response:download? |
| as_attachment=body_value!=0; | bool as_attachment=body_value!=0; |
| if(!body_value) | if(!body_value) |
| body_value=response.fields().get(body_name_upper); // $response:body | body_value=response.fields().get(body_name_upper); // $response:body |
| if(!body_value) | if(!body_value) |
| Line 483 void Request::core(const char* config_fi | Line 521 void Request::core(const char* config_fi |
| }); | }); |
| } | } |
| body_file=body_value->as_vfile(flang, &charsets); | // can throw exceptions while handling $response:download[] |
| output_result(body_value->as_vfile(flang, &charsets), header_only, as_attachment); | |
| } catch(const Exception& e) { // request handling problem | } catch(const Exception& e) { // request handling problem |
| try { | |
| // we're returning not result, but error explanation | |
| Request::Exception_details details=get_details(e); | // we're returning not result, but error explanation |
| const char* exception_cstr=get_exception_cstr(e, details); | Request::Exception_details details=get_details(e); |
| const char* exception_cstr=get_exception_cstr(e, details); | |
| // reset language to default | |
| flang=fdefault_lang; | // reset language to default |
| // reset response | flang=fdefault_lang; |
| response.fields().clear(); | // reset response |
| response.fields().clear(); | |
| SAPI::clear_headers(sapi_info); | |
| // this is what we'd return in $response:body | // this is what we'd return in $response:body |
| const String* body_string=0; | const String* body_string=0; |
| try { | |
| // maybe we'd be lucky enough as to report an error in a gracefull way... | // maybe we'd be lucky enough as to report an error in a gracefull way... |
| if(const Method *method=main_class.get_method(*new String(UNHANDLED_EXCEPTION_METHOD_NAME))) { | if(const Method *method=main_class.get_method(*new String(UNHANDLED_EXCEPTION_METHOD_NAME))) { |
| // preparing parameters to @unhandled_exception[exception;stack] | // preparing parameters to @unhandled_exception[exception;stack] |
| Line 520 void Request::core(const char* config_fi | Line 559 void Request::core(const char* config_fi |
| if(!vhandled || !vhandled->as_bool()) { | if(!vhandled || !vhandled->as_bool()) { |
| SAPI::log(sapi_info, "%s", exception_cstr); | SAPI::log(sapi_info, "%s", exception_cstr); |
| } | } |
| } catch(const Exception& e) { // exception in @unhandled_exception | |
| if(body_string) { // could report an error beautifully? | |
| VString body_vstring(*body_string); | |
| body_file=body_vstring.as_vfile(flang, &charsets); | |
| as_attachment=false; | |
| } else { | |
| // doing that ugly | |
| SAPI::send_error(sapi_info, exception_cstr, !strcmp(e.type(), "file.missing") ? "404" : "500"); | |
| return; | |
| } | |
| } catch(const Exception& e) { // exception in unhandled exception | |
| Request::Exception_details details=get_details(e); | Request::Exception_details details=get_details(e); |
| // unconditionally log the beast in exception handler | // logging both initial and new exceptions |
| throw Exception(0, 0, "Unhandled exception in %s", get_exception_cstr(e, details)); | SAPI::log(sapi_info, "%s", exception_cstr); |
| SAPI::log(sapi_info, "Exception in @unhandled_exception at %s", get_exception_cstr(e, details)); | |
| SAPI::send_error(sapi_info, "Exception in @unhandled_exception, details are available in Parser error log."); | |
| return; | |
| } | } |
| } | |
| // write out the result outside of try as network exceptions should not be handled by parser code. | if(body_string) { // could report an error beautifully? |
| output_result(body_file, header_only, as_attachment); | VString body_vstring(*body_string); |
| output_result(body_vstring.as_vfile(flang, &charsets), header_only, false); | |
| } else { | |
| // doing that ugly | |
| SAPI::send_error(sapi_info, exception_cstr, strcmp(e.type(), "file.missing") ? "500" : "404"); | |
| } | |
| } | |
| } | } |
| uint Request::register_file(String::Body file_spec) { | uint Request::register_file(String::Body file_spec) { |
| Line 622 void Request::use_file(const String& fil | Line 657 void Request::use_file(const String& fil |
| } else | } else |
| throw Exception(PARSER_RUNTIME, 0, "$" CLASS_PATH_NAME " must be string or table"); | throw Exception(PARSER_RUNTIME, 0, "$" CLASS_PATH_NAME " must be string or table"); |
| if(!filespec) | if(!filespec) |
| throw Exception(PARSER_RUNTIME, &file_name, "not found along $main:" CLASS_PATH_NAME); | throw Exception(PARSER_RUNTIME, &file_name, "not found along $MAIN:" CLASS_PATH_NAME); |
| } else | } else |
| throw Exception(PARSER_RUNTIME, &file_name, "usage failed - no $main:" CLASS_PATH_NAME " were specified"); | throw Exception(PARSER_RUNTIME, &file_name, "usage failed - no $MAIN:" CLASS_PATH_NAME " were specified"); |
| } | } |
| use_file_directly(*filespec, true, with_auto_p); | use_file_directly(*filespec, true, with_auto_p); |
| Line 660 void Request::use_buf(VStateless_class& | Line 695 void Request::use_buf(VStateless_class& |
| ArrayClass& cclasses=compile(&aclass, source, main_alias, file_no, line_no_offset); | ArrayClass& cclasses=compile(&aclass, source, main_alias, file_no, line_no_offset); |
| VString* vfilespec= | VString* vfilespec= |
| new VString(*new String(file_list[file_no], String::L_TAINTED)); | new VString(file_list[file_no]); |
| for(size_t i=0; i<cclasses.count(); i++){ | for(size_t i=0; i<cclasses.count(); i++){ |
| VStateless_class& cclass=*cclasses.get(i); | VStateless_class& cclass=*cclasses.get(i); |
| Line 670 void Request::use_buf(VStateless_class& | Line 705 void Request::use_buf(VStateless_class& |
| configure_admin(cclass/*, executed.method->name*/); | configure_admin(cclass/*, executed.method->name*/); |
| // locate and execute possible @auto[] static | // locate and execute possible @auto[] static |
| execute_method_if_exists(cclass, auto_method_name, vfilespec); | execute_auto_method_if_exists(cclass, auto_method_name, vfilespec); |
| cclass.enable_default_setter(); | cclass.enable_default_setter(); |
| } | } |
| Line 691 const String& Request::full_disk_path(co | Line 726 const String& Request::full_disk_path(co |
| result << relative_name; | result << relative_name; |
| return result; | return result; |
| } | } |
| if(relative_name.pos("://")!=STRING_NOT_FOUND // something like "http://xxx" | if(relative_name.starts_with("http://") || relative_name.starts_with("parser://") |
| #ifdef WIN32 | #ifdef WIN32 |
| || relative_name.pos(":")==1 // DRIVE: | || relative_name.pos(":")==1 // DRIVE: |
| || relative_name.starts_with("\\\\") // UNC1 | || relative_name.starts_with("\\\\") // UNC1 |
| Line 734 static void output_sole_piece(Request& r | Line 769 static void output_sole_piece(Request& r |
| output=Charset::transcode(output, r.charsets.source(), r.charsets.client()); | output=Charset::transcode(output, r.charsets.source(), r.charsets.client()); |
| // prepare header: Content-Length | // prepare header: Content-Length |
| SAPI::add_header_attribute(r.sapi_info, HTTP_CONTENT_LENGTH, format(output.length, "%u")); | SAPI::add_header_attribute(r.sapi_info, HTTP_CONTENT_LENGTH, pa_uitoa(output.length)); |
| // send header | // send header |
| SAPI::send_header(r.sapi_info); | SAPI::send_headers(r.sapi_info); |
| // send body | // send body |
| if(!header_only) | if(!header_only) |
| Line 766 static void parse_range(const String* s, | Line 801 static void parse_range(const String* s, |
| while(*p==' ' || *p=='\t') p++; | while(*p==' ' || *p=='\t') p++; |
| if(*p >= '0' && *p <= '9'){ | if(*p >= '0' && *p <= '9'){ |
| const char *s=p; | const char *n=p; |
| while(*p>='0' && *p<='9') p++; | while(*p>='0' && *p<='9') p++; |
| r.start = pa_atoul(pa_strdup(s,p-s)); | r.start = pa_atoul(pa_strdup(n, p-n)); |
| } | } |
| while(*p==' ' || *p=='\t') p++; | while(*p==' ' || *p=='\t') p++; |
| Line 778 static void parse_range(const String* s, | Line 813 static void parse_range(const String* s, |
| while(*p==' ' || *p=='\t') p++; | while(*p==' ' || *p=='\t') p++; |
| if(*p >= '0' && *p <= '9'){ | if(*p >= '0' && *p <= '9'){ |
| const char *s=p; | const char *n=p; |
| while(*p>='0' && *p<='9') p++; | while(*p>='0' && *p<='9') p++; |
| r.end = pa_atoul(pa_strdup(s,p-s)); | r.end = pa_atoul(pa_strdup(n, p-n)); |
| } | } |
| while(*p==' ' || *p=='\t') p++; | while(*p==' ' || *p=='\t') p++; |
| Line 801 struct Send_range_action_info { | Line 836 struct Send_range_action_info { |
| static void send_range(struct stat& /*finfo*/, int f, const String& /*file_spec*/, void *context){ | static void send_range(struct stat& /*finfo*/, int f, const String& /*file_spec*/, void *context){ |
| Send_range_action_info &info = *(Send_range_action_info*)context; | Send_range_action_info &info = *(Send_range_action_info*)context; |
| SAPI::send_header(info.r->sapi_info); | SAPI::send_headers(info.r->sapi_info); |
| pa_lseek(f, info.offset, SEEK_SET); | pa_lseek(f, info.offset, SEEK_SET); |
| const size_t BUFSIZE = 128*0x400; | char buf[FILE_BUFFER_SIZE]; |
| char buf[BUFSIZE]; | |
| do{ | do{ |
| size_t to_read = info.part_length < BUFSIZE ? (size_t)info.part_length : BUFSIZE; | size_t to_read = info.part_length < FILE_BUFFER_SIZE ? (size_t)info.part_length : FILE_BUFFER_SIZE; |
| size_t to_write = file_block_read(f, buf, to_read); | size_t to_write = file_block_read(f, buf, to_read); |
| if(to_write == 0) | if(to_write == 0) |
| Line 866 static void output_pieces(Request& r, bo | Line 900 static void output_pieces(Request& r, bo |
| } | } |
| } | } |
| SAPI::add_header_attribute(r.sapi_info, HTTP_CONTENT_LENGTH, format((double)part_length, "%.15g")); | SAPI::add_header_attribute(r.sapi_info, HTTP_CONTENT_LENGTH, pa_uitoa(part_length)); |
| if(add_last_modified) | if(add_last_modified) |
| SAPI::add_header_attribute(r.sapi_info, "last-modified", attributed_meaning_to_string(date, String::L_AS_IS, true).cstr()); | SAPI::add_header_attribute(r.sapi_info, "last-modified", attributed_meaning_to_string(date, String::L_AS_IS, true).cstr()); |
| if(header_only){ | if(header_only){ |
| SAPI::send_header(r.sapi_info); | SAPI::send_headers(r.sapi_info); |
| } else { | } else { |
| Send_range_action_info info = { &r, offset, part_length}; | Send_range_action_info info = { &r, offset, part_length}; |
| file_read_action_under_lock(r.full_disk_path(filename), "send", send_range, &info); | file_read_action_under_lock(r.full_disk_path(filename), "send", send_range, &info); |
| Line 896 void Request::output_result(VFile* body_ | Line 930 void Request::output_result(VFile* body_ |
| // Content-Disposition, use $.name[<empty>] to avoid | // Content-Disposition, use $.name[<empty>] to avoid |
| const String* disposition_name = sname ? sname->is_empty() ? NULL : sname : sfile; | const String* disposition_name = sname ? sname->is_empty() ? NULL : sname : sfile; |
| if(disposition_name) { | if(disposition_name) { |
| String& filename = *new String(pa_filename(disposition_name->cstr()), String::L_URI); | |
| String& filename_asterisk = *new String(charsets.client().NAME(), String::L_CLEAN) << (*new String("''")) << filename; | |
| VHash& hash=*new VHash(); | VHash& hash=*new VHash(); |
| hash.hash().put(value_name, new VString(as_attachment ? content_disposition_attachment : content_disposition_inline)); | hash.hash().put(value_name, new VString(as_attachment ? content_disposition_attachment : content_disposition_inline)); |
| hash.hash().put(content_disposition_filename_name, new VString(*new String(*disposition_name, String::L_HTTP_HEADER))); | hash.hash().put(content_disposition_filename_name, new VString(filename)); |
| hash.hash().put(content_disposition_filename_name_asterisk, new VString(filename_asterisk)); | |
| response.fields().put(content_disposition_name_upper, &hash); | response.fields().put(content_disposition_name_upper, &hash); |
| } | } |
| Line 931 void Request::output_result(VFile* body_ | Line 969 void Request::output_result(VFile* body_ |
| VDate* vdate=0; | VDate* vdate=0; |
| if(Value* v=body_file->fields().get("mdate")) { | if(Value* v=body_file->fields().get("mdate")) { |
| if(Value* vdatep=v->as(VDATE_TYPE)) | vdate=dynamic_cast<VDate*>(v); |
| vdate=static_cast<VDate*>(vdatep); | if(!vdate) |
| else | |
| throw Exception(PARSER_RUNTIME, 0, "mdate must be a date"); | throw Exception(PARSER_RUNTIME, 0, "mdate must be a date"); |
| } | } |
| if(!vdate) | if(!vdate) |
| Line 1013 Request::Exception_details Request::get_ | Line 1050 Request::Exception_details Request::get_ |
| // $.source | // $.source |
| if(problem_source) | if(problem_source) |
| hash.put(exception_source_part_name, new VString(*new String(*problem_source, String::L_TAINTED))); | hash.put(exception_source_part_name, new VString(*problem_source)); |
| // $.file $.lineno $.colno | // $.file $.lineno $.colno |
| if(origin.file_no){ | if(origin.file_no){ |
| hash.put("file", new VString(*new String(file_list[origin.file_no], String::L_TAINTED))); | HASH_PUT_CSTR(hash, "file", new VString(file_list[origin.file_no])); |
| hash.put("lineno", new VInt(1+origin.line)); | HASH_PUT_CSTR(hash, "lineno", new VInt(1+origin.line)); |
| hash.put("colno", new VInt(1+origin.col)); | HASH_PUT_CSTR(hash, "colno", new VInt(1+origin.col)); |
| } | } |
| // $.comment | // $.comment |
| if(const char* comment=e.comment(true)) | if(const char* comment=e.comment(true)) |
| hash.put(exception_comment_part_name, new VString(*new String(comment, String::L_TAINTED))); | hash.put(exception_comment_part_name, new VString(comment)); |
| // $.handled(0) | // $.handled(0) |
| hash.put(exception_handled_part_name, &VBool::get(false)); | hash.put(exception_handled_part_name, &VBool::get(false)); |