Diff for /parser3/src/main/pa_http.C between versions 1.71 and 1.72

version 1.71, 2016/07/21 17:05:37 version 1.72, 2016/07/26 13:20:23
Line 32  volatile const char * IDENT_PA_HTTP_C="$ Line 32  volatile const char * IDENT_PA_HTTP_C="$
 #define HTTP_ANY_STATUS_NAME    "any-status"  #define HTTP_ANY_STATUS_NAME    "any-status"
 #define HTTP_OMIT_POST_CHARSET_NAME     "omit-post-charset"     // ^file::load[...;http://...;$.method[post]] by default adds charset to content-type  #define HTTP_OMIT_POST_CHARSET_NAME     "omit-post-charset"     // ^file::load[...;http://...;$.method[post]] by default adds charset to content-type
   
 #define HTTP_TABLES_NAME "tables"  
   
 #define HTTP_USER "user"  #define HTTP_USER "user"
 #define HTTP_PASSWORD "password"  #define HTTP_PASSWORD "password"
   
Line 617  Table* parse_cookies(Request& r, Table * Line 615  Table* parse_cookies(Request& r, Table *
         return &result;          return &result;
 }  }
   
   void *tables_update(HashStringValue& tables, const String::Body name, const String& value){
           Table *table;
           if(Value *valready=tables.get(name)) {
                   // second+ appearence
                   table=valready->get_table();
           } else {
                   // first appearence
                   Table::columns_type columns=new ArrayString(1);
                   *columns+=new String("value");
                   table=new Table(columns);
                   tables.put(name, new VTable(table));
           }
           // this string becomes next row
           ArrayString& row=*new ArrayString(1);
           row+=&value;
           *table+=&row;
   }
   
 /// @todo build .cookies field. use ^file.tables.SET-COOKIES.menu{ for now  /// @todo build .cookies field. use ^file.tables.SET-COOKIES.menu{ for now
 File_read_http_result pa_internal_file_read_http(Request& r,  File_read_http_result pa_internal_file_read_http(Request& r, const String& file_spec, bool as_text, HashStringValue *options, bool transcode_text_result) {
                                                 const String& file_spec,  
                                                 bool as_text,  
                                                 HashStringValue *options,  
                                                 bool transcode_text_result) {  
         File_read_http_result result;          File_read_http_result result;
         char host[MAX_STRING];          char host[MAX_STRING];
         const char *idna_host;          const char *idna_host;
Line 637  File_read_http_result pa_internal_file_r Line 649  File_read_http_result pa_internal_file_r
         Value* vheaders=0;          Value* vheaders=0;
         Value* vcookies=0;          Value* vcookies=0;
         Value* vbody=0;          Value* vbody=0;
         Charset *asked_remote_charset=0;          Charset* asked_remote_charset=0;
         Charset* real_remote_charset=0;          Charset* real_remote_charset=0;
         const char* user_cstr=0;          const char* user_cstr=0;
         const char* password_cstr=0;          const char* password_cstr=0;
Line 705  File_read_http_result pa_internal_file_r Line 717  File_read_http_result pa_internal_file_r
   
         if(encode){          if(encode){
                 if(method_is_get)                  if(method_is_get)
                         throw Exception(PARSER_RUNTIME,                          throw Exception(PARSER_RUNTIME, 0, "you can not use $." HTTP_FORM_ENCTYPE_NAME " option with method GET");
                                 0,  
                                 "you can not use $." HTTP_FORM_ENCTYPE_NAME " option with method GET");  
   
                 multipart=strcasecmp(encode, HTTP_CONTENT_TYPE_MULTIPART_FORMDATA)==0;                  multipart=strcasecmp(encode, HTTP_CONTENT_TYPE_MULTIPART_FORMDATA)==0;
   
                 if(!multipart && strcasecmp(encode, HTTP_CONTENT_TYPE_FORM_URLENCODED)!=0)                  if(!multipart && strcasecmp(encode, HTTP_CONTENT_TYPE_FORM_URLENCODED)!=0)
                         throw Exception(PARSER_RUNTIME,                          throw Exception(PARSER_RUNTIME, 0, "$." HTTP_FORM_ENCTYPE_NAME " option value can be " HTTP_CONTENT_TYPE_FORM_URLENCODED " or " HTTP_CONTENT_TYPE_MULTIPART_FORMDATA " only");
                                 0,  
                                 "$." HTTP_FORM_ENCTYPE_NAME " option value can be " HTTP_CONTENT_TYPE_FORM_URLENCODED " or " HTTP_CONTENT_TYPE_MULTIPART_FORMDATA " only");  
         }          }
   
         if(vbody){          if(vbody){
                 if(method_is_get)                  if(method_is_get)
                         throw Exception(PARSER_RUNTIME,                          throw Exception(PARSER_RUNTIME, 0, "you can not use $." HTTP_BODY_NAME " option with method GET");
                                 0,  
                                 "you can not use $." HTTP_BODY_NAME " option with method GET");  
   
                 if(form)                  if(form)
                         throw Exception(PARSER_RUNTIME,                          throw Exception(PARSER_RUNTIME, 0, "you can not use options $." HTTP_BODY_NAME " and $." HTTP_FORM_NAME " together");
                                 0,  
                                 "you can not use options $." HTTP_BODY_NAME " and $." HTTP_FORM_NAME " together");  
         }          }
   
         //preparing request          //preparing request
Line 742  File_read_http_result pa_internal_file_r Line 746  File_read_http_result pa_internal_file_r
   
                 const char* current=connect_string_cstr;                  const char* current=connect_string_cstr;
                 if(strncmp(current, "http://", 7)!=0)                  if(strncmp(current, "http://", 7)!=0)
                         throw Exception(PARSER_RUNTIME,                           throw Exception(PARSER_RUNTIME, &connect_string, "does not start with http://"); //never
                                 &connect_string,   
                                 "does not start with http://"); //never  
                 current+=7;                  current+=7;
   
                 strncpy(host, current, sizeof(host)-1);  host[sizeof(host)-1]=0;                  strncpy(host, current, sizeof(host)-1);  host[sizeof(host)-1]=0;
Line 788  File_read_http_result pa_internal_file_r Line 790  File_read_http_result pa_internal_file_r
                                         &content_type_url_encoded};                                          &content_type_url_encoded};
                                 headers->for_each<Http_pass_header_info*>(http_pass_header, &info);                                   headers->for_each<Http_pass_header_info*>(http_pass_header, &info); 
                         } else                          } else
                                 throw Exception(PARSER_RUNTIME,                                   throw Exception(PARSER_RUNTIME, 0, "headers param must be hash"); 
                                         0,  
                                         "headers param must be hash");   
                 };                  };
   
                 const char* request_body=0;                  const char* request_body=0;
Line 816  File_read_http_result pa_internal_file_r Line 816  File_read_http_result pa_internal_file_r
                                 request_body=vbody->as_string().untaint_and_transcode_cstr(String::L_URI, &(r.charsets));                                  request_body=vbody->as_string().untaint_and_transcode_cstr(String::L_URI, &(r.charsets));
                         } else {                          } else {
                                 // content-type != application/x-www-form-urlencoded -> transcode only, don't url-encode!                                  // content-type != application/x-www-form-urlencoded -> transcode only, don't url-encode!
                                 request_body=Charset::transcode(                                  const String &sbody=vbody->as_string();
                                         String::C(vbody->as_string().cstr(), vbody->as_string().length()),                                  request_body=Charset::transcode(String::C(sbody.cstr(), sbody.length()), r.charsets.source(), *asked_remote_charset).str;
                                         r.charsets.source(),  
                                         *asked_remote_charset  
                                 ).str;  
                         }                          }
                         post_size=strlen(request_body);                          post_size=strlen(request_body);
                 }                  }
Line 835  File_read_http_result pa_internal_file_r Line 832  File_read_http_result pa_internal_file_r
                         head << "User-Agent: " DEFAULT_USER_AGENT CRLF;                          head << "User-Agent: " DEFAULT_USER_AGENT CRLF;
   
                 if(form && !method_is_get && content_type_specified) // POST + form + content-type was specified                  if(form && !method_is_get && content_type_specified) // POST + form + content-type was specified
                         throw Exception(PARSER_RUNTIME,                          throw Exception(PARSER_RUNTIME, 0, "$.content-type can't be specified with method POST"); 
                                 0,  
                                 "$.content-type can't be specified with method POST");   
   
                 if(vcookies && !vcookies->is_string()){ // allow empty                  if(vcookies && !vcookies->is_string()){ // allow empty
                         if(HashStringValue* cookies=vcookies->get_hash()) {                          if(HashStringValue* cookies=vcookies->get_hash()) {
Line 846  File_read_http_result pa_internal_file_r Line 841  File_read_http_result pa_internal_file_r
                                 cookies->for_each<Http_pass_header_info*>(http_pass_cookie, &info);                                   cookies->for_each<Http_pass_header_info*>(http_pass_cookie, &info); 
                                 head << CRLF;                                  head << CRLF;
                         } else                          } else
                                 throw Exception(PARSER_RUNTIME,                                   throw Exception(PARSER_RUNTIME, 0, "cookies param must be hash");
                                         0,  
                                         "cookies param must be hash");  
                 }                  }
   
                 if(request_body)                  if(request_body)
Line 871  File_read_http_result pa_internal_file_r Line 864  File_read_http_result pa_internal_file_r
                 }                  }
         }          }
                   
         char* response;          char* response_str;
         size_t response_size;          size_t response_size;
   
         // sending request          // sending request
         int status_code=http_request(response, response_size,          int status_code=http_request(response_str, response_size, idna_host, port, request, request_size, timeout_secs, fail_on_status_ne_200);
                 idna_host, port, request, request_size,  
                 timeout_secs, fail_on_status_ne_200);   
                   
         // processing results             // processing results
         char* raw_body; size_t raw_body_size;          char* raw_body; size_t raw_body_size;
         char* headers_end_at;          char* headers_end_at;
         find_headers_end(response,           find_headers_end(response_str, headers_end_at, raw_body);
                 headers_end_at,          raw_body_size=response_size-(raw_body-response_str);
                 raw_body);  
         raw_body_size=response_size-(raw_body-response);  
                   
         result.headers=new HashStringValue;          result.headers=new HashStringValue;
         VHash* vtables=new VHash;          VHash* vtables=new VHash;
         result.headers->put(HTTP_TABLES_NAME, vtables);          result.headers->put("tables", vtables);
   
           ResponseHeaders response;
   
         if(headers_end_at) {          if(headers_end_at) {
                 *headers_end_at=0;                  *headers_end_at=0;
                 const String header_block(String::C(response, headers_end_at-response), String::L_TAINTED);                  const String header_block(String::C(response_str, headers_end_at-response_str), String::L_TAINTED);
                                   
                 ArrayString aheaders;                  ArrayString aheaders;
                 HashStringValue& tables=vtables->hash();  
   
                 size_t pos_after=0;                  size_t pos_after=0;
                 header_block.split(aheaders, pos_after, "\n");                   header_block.split(aheaders, pos_after, "\n"); 
                   
                 // processing headers  
                 size_t aheaders_count=aheaders.count();  
                 for(size_t i=1; i<aheaders_count; i++) {  
                         const String& line=*aheaders.get(i);  
                         size_t pos=line.pos(':');   
                         if(pos==STRING_NOT_FOUND || pos<1)  
                                 throw Exception("http.response",   
                                         &connect_string,  
                                         "bad response from host - bad header \"%s\"", line.cstr());  
                         const String::Body HEADER_NAME=line.mid(0, pos).change_case(r.charsets.source(), String::CC_UPPER);  
                         const String& HEADER_VALUE=line.mid(pos+1, line.length()).trim(String::TRIM_BOTH, " \t\r");  
                         if(as_text && HEADER_NAME==HTTP_CONTENT_TYPE_UPPER && !real_remote_charset)  
                                 real_remote_charset=detect_charset(HEADER_VALUE.cstr());  
   
                         // tables  
                         {  
                                 Value *valready=(Value *)tables.get(HEADER_NAME);  
                                 bool existed=valready!=0;  
                                 Table *table;  
                                 if(existed) {  
                                         // second+ appearence  
                                         table=valready->get_table();  
                                 } else {  
                                         // first appearence  
                                         Table::columns_type columns=new ArrayString(1);  
                                         *columns+=new String("value");  
                                         table=new Table(columns);  
                                 }  
                                 // this string becomes next row  
                                 ArrayString& row=*new ArrayString(1);  
                                 row+=&HEADER_VALUE;  
                                 *table+=&row;  
                                 // not existed before? add it  
                                 if(!existed)  
                                         tables.put(HEADER_NAME, new VTable(table));  
                         }  
   
                         result.headers->put(HEADER_NAME, new VString(HEADER_VALUE));                  Array_iterator<const String*> i(aheaders);
                   i.next(); // skipping status
                   for(;i.has_next();){
                           const char *line=i.next()->cstr();
                           if(!response.add_header(line))
                                   throw Exception("http.response", &connect_string, "bad response from host - bad header \"%s\"", line);
                 }                  }
   
                 // filling $.cookies  
                 if(Value *vcookies=(Value *)tables.get("SET-COOKIE"))  
                         result.headers->put(HTTP_COOKIES_NAME, new VTable(parse_cookies(r, vcookies->get_table())));  
         }          }
   
         if(as_text){          if (!real_remote_charset && !response.content_type.is_empty())
                   real_remote_charset= detect_charset(response.content_type.cstr());
   
           if(as_text)
                 real_remote_charset=charsets.checkBOM(raw_body, raw_body_size, real_remote_charset);                  real_remote_charset=charsets.checkBOM(raw_body, raw_body_size, real_remote_charset);
   
           if (!real_remote_charset)
                   real_remote_charset=asked_remote_charset; // never null
   
           for(Array_iterator<ResponseHeaders::Header> i(response.headers); i.has_next(); ){
                   ResponseHeaders::Header header=i.next();
   
                   header.transcode(*real_remote_charset, r.charsets.source());
   
                   String &header_value=*new String(header.value, String::L_TAINTED);
   
                   tables_update(vtables->hash(), header.name, header_value);
                   result.headers->put(header.name, new VString(header_value));
         }          }
   
           // filling $.cookies
           if(Value *vcookies=vtables->hash().get("SET-COOKIE"))
                   result.headers->put(HTTP_COOKIES_NAME, new VTable(parse_cookies(r, vcookies->get_table())));
   
         // output response          // output response
         String::C real_body=String::C(raw_body, raw_body_size);          String::C real_body=String::C(raw_body, raw_body_size);
   
         if(as_text && transcode_text_result && raw_body_size) { // raw_body_size must be checked because transcode returns CONST string in case length==0, which contradicts hacking few lines below          if(as_text && transcode_text_result && raw_body_size) { // raw_body_size must be checked because transcode returns CONST string in case length==0, which contradicts hacking few lines below
                 // defaulting to used-asked charset [it's never empty!]  
                 if(!real_remote_charset)  
                         real_remote_charset=asked_remote_charset;  
   
                 real_body=Charset::transcode(real_body, *real_remote_charset, r.charsets.source());                  real_body=Charset::transcode(real_body, *real_remote_charset, r.charsets.source());
   
         }          }
   
         result.str=const_cast<char *>(real_body.str); // hacking a little          result.str=const_cast<char *>(real_body.str); // hacking a little

Removed from v.1.71  
changed lines
  Added in v.1.72


E-mail: