--- parser3/src/classes/curl.C 2013/04/21 21:24:53 1.26 +++ parser3/src/classes/curl.C 2015/10/26 01:21:53 1.35 @@ -1,7 +1,7 @@ /** @file Parser: @b curl parser class. - Copyright (c) 2001-2012 Art. Lebedev Studio (http://www.artlebedev.com) + Copyright (c) 2001-2015 Art. Lebedev Studio (http://www.artlebedev.com) */ #include "pa_config_includes.h" @@ -11,12 +11,13 @@ #include "pa_vfile.h" #include "pa_charsets.h" #include "pa_vstring.h" +#include "pa_vdate.h" #include "pa_vtable.h" #include "pa_common.h" #include "pa_http.h" #include "ltdl.h" -volatile const char * IDENT_CURL_C="$Id: curl.C,v 1.26 2013/04/21 21:24:53 moko Exp $"; +volatile const char * IDENT_CURL_C="$Id: curl.C,v 1.35 2015/10/26 01:21:53 moko Exp $"; class MCurl: public Methoded { public: @@ -44,8 +45,7 @@ typedef void (*t_curl_formfree)(struct c #define DLINK(name) GLINK(name) if(!f_##name) return "function " #name " was not found"; static const char *dlink(const char *dlopen_file_spec) { - if(lt_dlinit()) - return lt_dlerror(); + pa_dlinit(); lt_dlhandle handle=lt_dlopen(dlopen_file_spec); @@ -112,6 +112,7 @@ public: fcurl = f_curl_easy_init(); foptions = new ParserOptions(); f_curl_easy_setopt(fcurl, CURLOPT_POSTFIELDSIZE, 0); // fix libcurl bug + f_curl_easy_setopt(fcurl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); // avoid ipv6 by default } ~Temp_curl() { f_curl_easy_cleanup(fcurl); @@ -122,11 +123,7 @@ public: }; bool curl_linked = false; -#ifdef WIN32 -const char *curl_library="libcurl.dll"; -#else -const char *curl_library="libcurl.so"; -#endif +const char *curl_library="libcurl" LT_MODULE_EXT; const char *curl_status = 0; @@ -230,6 +227,7 @@ public: CURL_OPT(CURL_INT, FOLLOWLOCATION); CURL_OPT(CURL_INT, UNRESTRICTED_AUTH); + CURL_OPT(CURL_INT, IPRESOLVE); CURL_OPT(CURL_INT, POST); CURL_OPT(CURL_INT, HTTPGET); @@ -301,6 +299,54 @@ public: } *curl_options=0; +class CurlInfo { +public: + + enum OptionType { + CURL_STRING, + CURL_INT, + CURL_DOUBLE + }; + + CURLINFO id; + OptionType type; + CurlInfo(CURLINFO aid, OptionType atype): id(aid), type(atype) {} +}; + +class CurlInfoHash: public HashString { +public: + CurlInfoHash() { +#define CURL_INF(type, name) put(str_lower(#name),new CurlInfo(CURLINFO_##name, CurlInfo::type)); + CURL_INF(CURL_DOUBLE, APPCONNECT_TIME); + CURL_INF(CURL_DOUBLE, CONNECT_TIME); + CURL_INF(CURL_DOUBLE, CONTENT_LENGTH_DOWNLOAD); + CURL_INF(CURL_DOUBLE, CONTENT_LENGTH_UPLOAD); + CURL_INF(CURL_STRING, CONTENT_TYPE); + CURL_INF(CURL_STRING, EFFECTIVE_URL); + CURL_INF(CURL_INT, HEADER_SIZE); + CURL_INF(CURL_INT, HTTPAUTH_AVAIL); + CURL_INF(CURL_DOUBLE, NAMELOOKUP_TIME); + CURL_INF(CURL_INT, NUM_CONNECTS); + CURL_INF(CURL_INT, OS_ERRNO); + CURL_INF(CURL_DOUBLE, PRETRANSFER_TIME); + CURL_INF(CURL_STRING, PRIMARY_IP); + CURL_INF(CURL_INT, PROXYAUTH_AVAIL); + CURL_INF(CURL_INT, REDIRECT_COUNT); + CURL_INF(CURL_DOUBLE, REDIRECT_TIME); + CURL_INF(CURL_STRING, REDIRECT_URL); + CURL_INF(CURL_INT, REQUEST_SIZE); + CURL_INF(CURL_INT, RESPONSE_CODE); + CURL_INF(CURL_DOUBLE, SIZE_DOWNLOAD); + CURL_INF(CURL_DOUBLE, SIZE_UPLOAD); + CURL_INF(CURL_DOUBLE, SPEED_DOWNLOAD); + CURL_INF(CURL_DOUBLE, SPEED_UPLOAD); + CURL_INF(CURL_INT, SSL_VERIFYRESULT); + CURL_INF(CURL_DOUBLE, STARTTRANSFER_TIME); + CURL_INF(CURL_DOUBLE, TOTAL_TIME); + } + +} *curl_infos=0; + static const char *curl_urlencode(const String &s, Request& r){ if(options().charset){ Temp_client_charset temp(r.charsets, *options().charset); @@ -477,8 +523,7 @@ static void curl_setopt(HashStringValue: break; } case CurlOption::PARSER_CHARSET:{ - // 'charset' parser option - options().charset=&::charsets.get(v.as_string().change_case(r.charsets.source(), String::CC_UPPER)); + // 'charset' parser option should be processed before other options break; } case CurlOption::PARSER_RESPONSE_CHARSET:{ @@ -496,8 +541,59 @@ static void _curl_options(Request& r, Me if(curl_options==0) curl_options=new CurlOptionHash(); - if(HashStringValue* options=params.as_hash(0)) - options->for_each(curl_setopt, r); + if(HashStringValue* options_hash=params.as_hash(0)){ + if(Value* value=options_hash->get("charset")){ + // charset should be handled first as params may require transcode + Value &v=r.process_to_value(*value); + options().charset=&::charsets.get(v.as_string().change_case(r.charsets.source(), String::CC_UPPER)); + } + options_hash->for_each(curl_setopt, r); + } +} + +#define CURL_GETINFO(arg) \ + if((res=f_curl_easy_getinfo(curl(), info->id, &arg)) != CURLE_OK){ \ + throw Exception("curl", 0, "failed to get %s info: %s", key.cstr(), f_curl_easy_strerror(res)); \ + } + +static Value *curl_getinfo(const String::Body &key, CurlInfo *info=0) { + if(info==0 && !(info=curl_infos->get(key))) + throw Exception("curl", 0, "called with invalid parameter '%s'", key.cstr()); + + CURLcode res; + switch (info->type){ + case CurlInfo::CURL_STRING:{ + char *str=0; + CURL_GETINFO(str); + return new VString(str ? *new String(pa_strdup(str), String::L_TAINTED) : String::Empty); + } + case CurlInfo::CURL_INT:{ + long l=0; + CURL_GETINFO(l); + return new VInt(l); + } + case CurlInfo::CURL_DOUBLE:{ + double d=0; + CURL_GETINFO(d); + return new VDouble(d); + } + } + return VVoid::get(); +} + +static void _curl_info(Request& r, MethodParams& params){ + if(curl_infos==0) + curl_infos=new CurlInfoHash(); + if(params.count()==1){ + const String &name=params.as_string(0, "name must be string"); + r.write_assign_lang(*curl_getinfo(name)); + } else { + VHash& result=*new VHash; + for(CurlInfoHash::Iterator i(*curl_infos); i; i.next() ){ + result.get_hash()->put(i.key(), curl_getinfo(i.key(), i.value())); + } + r.write_no_lang(result); + } } @@ -588,6 +684,7 @@ static void _curl_load_action(Request& r case CURLE_SSL_CACERT: case CURLE_SSL_ENGINE_INITFAILED: ex_type = "curl.ssl"; break; + default: break; } throw Exception( ex_type ? ex_type : "curl.fail", 0, "%s", f_curl_easy_strerror(res)); } @@ -659,5 +756,6 @@ MCurl::MCurl(): Methoded("curl") { add_native_method("session", Method::CT_STATIC, _curl_session, 1, 1); add_native_method("version", Method::CT_STATIC, _curl_version, 0, 0); add_native_method("options", Method::CT_STATIC, _curl_options, 1, 1); + add_native_method("info", Method::CT_STATIC, _curl_info, 0, 1); add_native_method("load", Method::CT_STATIC, _curl_load, 0, 1); }