|
|
| version 1.65, 2020/11/10 22:42:24 | version 1.79, 2026/07/13 21:42:16 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: @b curl parser class. | Parser: @b curl parser class. |
| Copyright (c) 2001-2017 Art. Lebedev Studio (http://www.artlebedev.com) | Copyright (c) 2001-2026 Art. Lebedev Studio (https://www.artlebedev.com) |
| Authors: Konstantin Morshnev <moko@design.ru> | |
| */ | */ |
| #include "pa_config_includes.h" | #include "pa_config_includes.h" |
| Line 44 typedef void (*t_curl_formfree)(struct c | Line 45 typedef void (*t_curl_formfree)(struct c |
| #define GLINK(name) f_##name=(t_##name)lt_dlsym(handle, #name); | #define GLINK(name) f_##name=(t_##name)lt_dlsym(handle, #name); |
| #define DLINK(name) GLINK(name) if(!f_##name) return "function " #name " was not found"; | #define DLINK(name) GLINK(name) if(!f_##name) return "function " #name " was not found"; |
| static const char *dlink(const char *dlopen_file_spec) { | static const char *dlink(char *dlopen_file_spec) { |
| pa_dlinit(); | pa_dlinit(); |
| lt_dlhandle handle=lt_dlopen(dlopen_file_spec); | lt_dlhandle handle; |
| do { | |
| char *next=lsplit(dlopen_file_spec, ','); | |
| handle=lt_dlopen(dlopen_file_spec); | |
| dlopen_file_spec=next; | |
| } while (!handle && dlopen_file_spec); | |
| if(!handle){ | if(!handle){ |
| if(const char* result=lt_dlerror()) | if(const char* result=lt_dlerror()) |
| return result; | return result; |
| return "can not open the dynamic link module"; | return "cannot open the dynamic link module"; |
| } | } |
| DLINK(curl_easy_init); | DLINK(curl_easy_init); |
| Line 116 static ParserOptions &options(){ | Line 122 static ParserOptions &options(){ |
| class Temp_curl { | class Temp_curl { |
| CURL *saved_curl; | CURL *saved_curl; |
| ParserOptions *saved_options; | ParserOptions *saved_options; |
| // every TLS should be referenced elsewhere, or GC will collect it | |
| CURL *thread_curl; | |
| ParserOptions *thread_options; | |
| public: | public: |
| Temp_curl() : saved_curl(fcurl), saved_options(foptions){ | Temp_curl() : saved_curl(fcurl), saved_options(foptions){ |
| fcurl = f_curl_easy_init(); | thread_curl = fcurl = f_curl_easy_init(); |
| foptions = new ParserOptions(); | thread_options = foptions = new ParserOptions(); |
| f_curl_easy_setopt(fcurl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); // avoid ipv6 by default | f_curl_easy_setopt(fcurl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); // avoid ipv6 by default |
| } | } |
| Line 131 public: | Line 141 public: |
| } | } |
| }; | }; |
| #ifdef WIN32 | |
| #define CURL_LIBRARY "libcurl" LT_MODULE_EXT | |
| #else | |
| #define CURL_LIBRARY "libcurl" LT_MODULE_EXT ",libcurl" LT_MODULE_EXT ".4" | |
| #endif | |
| bool curl_linked = false; | bool curl_linked = false; |
| const char *curl_status = 0; | const char *curl_status = 0; |
| const char *curl_library="libcurl" LT_MODULE_EXT; | |
| const char *curl_library=CURL_LIBRARY; | |
| static void temp_curl(void (*action)(Request&, MethodParams&), Request& r, MethodParams& params){ | static void temp_curl(void (*action)(Request&, MethodParams&), Request& r, MethodParams& params){ |
| if(!curl_linked) | if(!curl_linked) |
| curl_status=dlink(curl_library); | curl_status=dlink(pa_strdup(curl_library)); |
| if(curl_status == 0){ | if(curl_status == 0){ |
| curl_linked=true; | curl_linked=true; |
| Temp_curl temp_curl; | Temp_curl temp_curl; |
| action(r,params); | action(r,params); |
| } else { | } else { |
| throw Exception("curl", 0, "failed to load curl library %s: %s", curl_library, curl_status); | const char *hint=strcmp(curl_library, CURL_LIBRARY) ? "" : " (before use, call ^curl:options[ $.library[correct.libcurl" LT_MODULE_EXT ".name] ])"; |
| throw Exception("curl", 0, "failed to load curl library %s%s", curl_status, hint); | |
| } | } |
| } | } |
| Line 158 static void _curl_session(Request& r, Me | Line 176 static void _curl_session(Request& r, Me |
| } | } |
| static void _curl_version_action(Request& r, MethodParams& ){ | static void _curl_version_action(Request& r, MethodParams& ){ |
| r.write(*new VString(*new String(f_curl_version(), String::L_TAINTED))); | r.write(*new VString(f_curl_version())); |
| } | } |
| static void _curl_version(Request& r, MethodParams& params){ | static void _curl_version(Request& r, MethodParams& params){ |
| Line 393 static void curl_form(HashStringValue *v | Line 411 static void curl_form(HashStringValue *v |
| CURLFORM_PTRCONTENTS, curl_transcode(String(tvalue->get(t)->get(0)->cstr()), r), | CURLFORM_PTRCONTENTS, curl_transcode(String(tvalue->get(t)->get(0)->cstr()), r), |
| CURLFORM_END); | CURLFORM_END); |
| } | } |
| } else if(VFile* fvalue=static_cast<VFile *>(i.value()->as("file"))){ | } else if(VFile* fvalue=dynamic_cast<VFile *>(i.value())){ |
| // file | // file |
| f_curl_formadd(&options().f_post, &f_last, | f_curl_formadd(&options().f_post, &f_last, |
| CURLFORM_PTRNAME, key, | CURLFORM_PTRNAME, key, |
| Line 495 static void curl_setopt(HashStringValue: | Line 513 static void curl_setopt(HashStringValue: |
| if( (res=f_curl_easy_setopt(curl(), CURLOPT_POSTFIELDSIZE, -1L)) == CURLE_OK ) | if( (res=f_curl_easy_setopt(curl(), CURLOPT_POSTFIELDSIZE, -1L)) == CURLE_OK ) |
| res=f_curl_easy_setopt(curl(), opt->id, curl_urlencode(v.as_string(), r)); | res=f_curl_easy_setopt(curl(), opt->id, curl_urlencode(v.as_string(), r)); |
| } else { | } else { |
| VFile *file=v.as_vfile(String::L_AS_IS); | VFile *file=v.as_vfile(); |
| if( (res=f_curl_easy_setopt(curl(), CURLOPT_POSTFIELDSIZE, (long)file->value_size())) == CURLE_OK ) | if( (res=f_curl_easy_setopt(curl(), CURLOPT_POSTFIELDSIZE, (long)file->value_size())) == CURLE_OK ) |
| res=f_curl_easy_setopt(curl(), opt->id, file->value_ptr()); | res=f_curl_easy_setopt(curl(), opt->id, file->value_ptr()); |
| } | } |
| Line 548 static void curl_setopt(HashStringValue: | Line 566 static void curl_setopt(HashStringValue: |
| // 'library' parser option | // 'library' parser option |
| if(!curl_linked){ | if(!curl_linked){ |
| curl_library=v.as_string().taint_cstr(String::L_FILE_SPEC); | curl_library=v.as_string().taint_cstr(String::L_FILE_SPEC); |
| if(!curl_library[0]) | |
| curl_library=CURL_LIBRARY; | |
| } else | } else |
| throw Exception("curl", 0, "failed to set option '%s': already loaded", key.cstr()); | throw Exception("curl", 0, "failed to set option '%s': already loaded", key.cstr()); |
| break; | break; |
| Line 624 static Value *curl_getinfo(const String: | Line 644 static Value *curl_getinfo(const String: |
| case CurlInfo::CURL_HTTP_VERSION:{ | case CurlInfo::CURL_HTTP_VERSION:{ |
| long l=0; | long l=0; |
| CURL_GETINFO(l); | CURL_GETINFO(l); |
| return new VString(*new String(curl_http_version_name(l), String::L_TAINTED)); | return new VString(curl_http_version_name(l)); |
| } | } |
| } | } |
| return VVoid::get(); | return VVoid::get(); |
| Line 719 static void _curl_load_action(Request& r | Line 739 static void _curl_load_action(Request& r |
| CURL_SETOPT(CURLOPT_WRITEFUNCTION, curl_writer, "curl writer function"); | CURL_SETOPT(CURLOPT_WRITEFUNCTION, curl_writer, "curl writer function"); |
| CURL_SETOPT(CURLOPT_WRITEDATA, &body, "curl write buffer"); | CURL_SETOPT(CURLOPT_WRITEDATA, &body, "curl write buffer"); |
| char error[CURL_ERROR_SIZE+1]=""; | |
| CURL_SETOPT(CURLOPT_ERRORBUFFER, error, "curl error buffer"); | |
| if(options().is_post && !options().has_content_length){ | if(options().is_post && !options().has_content_length){ |
| // libcurl bug walkaround. Prior to 7.38 (Debian Jessie) curl passed Content-length: -1 | // libcurl bug walkaround. Prior to 7.38 (Debian Jessie) curl passed Content-length: -1 |
| // after that no Content-length header is passed, that hangs request to nginx. | // after that no Content-length header is passed, that hangs request to nginx. |
| Line 747 static void _curl_load_action(Request& r | Line 770 static void _curl_load_action(Request& r |
| check_file_size(response.content_length, new String(options().url)); break; | check_file_size(response.content_length, new String(options().url)); break; |
| default: break; | default: break; |
| } | } |
| throw Exception( PA_DEFAULT(ex_type, "curl.fail"), new String(options().url), "%s", f_curl_easy_strerror(res)); | throw Exception( PA_DEFAULT(ex_type, "curl.fail"), new String(options().url), "%s", error[0] ? error : f_curl_easy_strerror(res)); |
| } | } |
| // assure trailing zero | // assure trailing zero |
| Line 778 static void _curl_load_action(Request& r | Line 801 static void _curl_load_action(Request& r |
| long http_status = 0; | long http_status = 0; |
| if(f_curl_easy_getinfo(curl(), CURLINFO_RESPONSE_CODE, &http_status) == CURLE_OK){ | if(f_curl_easy_getinfo(curl(), CURLINFO_RESPONSE_CODE, &http_status) == CURLE_OK){ |
| result.fields().put("status", new VInt(http_status)); | HASH_PUT_CSTR(result.fields(), "status", new VInt(http_status)); |
| } | } |
| VHash* vtables=new VHash; | VHash* vtables=new VHash; |
| result.fields().put("tables", vtables); | HASH_PUT_CSTR(result.fields(), "tables", vtables); |
| for(Array_iterator<HTTP_Headers::Header> i(response.headers); i.has_next(); ){ | for(Array_iterator<HTTP_Headers::Header> i(response.headers); i; ){ |
| HTTP_Headers::Header header=i.next(); | HTTP_Headers::Header header=i.next(); |
| if(asked_charset) | if(asked_charset) |
| Line 798 static void _curl_load_action(Request& r | Line 821 static void _curl_load_action(Request& r |
| // filling $.cookies | // filling $.cookies |
| if(Value *vcookies=vtables->hash().get("SET-COOKIE")) | if(Value *vcookies=vtables->hash().get("SET-COOKIE")) |
| result.fields().put(HTTP_COOKIES_NAME, new VTable(parse_cookies(r, vcookies->get_table()))); | HASH_PUT_CSTR(result.fields(), HTTP_COOKIES_NAME, new VTable(parse_cookies(r, vcookies->get_table()))); |
| r.write(result); | r.write(result); |
| } | } |