/* Try to fix connection parameters (called for an unconnected connector) */ static int/*bool*/ s_Adjust(SHttpConnector* uuu, char** retry, EReadMode read_mode) { assert(!uuu->sock && uuu->can_connect != eCC_None); /* we're here because something is going wrong */ if (++uuu->failure_count >= uuu->net_info->max_try) { if (*retry) free(*retry); if (read_mode != eRM_DropUnread && uuu->failure_count > 1) { CORE_LOGF_X(1, eLOG_Error, ("[HTTP] Too many failed attempts (%d)," " giving up", uuu->failure_count)); } uuu->can_connect = eCC_None; return 0/*failure*/; } /* adjust info before yet another connection attempt */ if (*retry) { int fail/*parse*/; assert(**retry); if (**retry != '?') { if (uuu->net_info->req_method == eReqMethod_Get || !uuu->w_len || (uuu->flags & fHCC_InsecureRedirect)) { int/*bool*/secure = uuu->net_info->scheme == eURL_Https ? 1 : 0; *uuu->net_info->args = '\0'/*arguments not inherited*/; fail = !ConnNetInfo_ParseURL(uuu->net_info, *retry); if (!fail && secure && uuu->net_info->scheme != eURL_Https && !(uuu->flags & fHCC_InsecureRedirect)) { fail = -1; } } else fail = -1; } else fail = 1; if (fail) { CORE_LOGF_X(2, eLOG_Error, ("[HTTP] %s to redirect to \"%s\"", fail < 0 ? "Prohibited" : "Unable", *retry)); } free(*retry); if (fail) { uuu->can_connect = eCC_None; return 0/*failure*/; } } else if (!uuu->adjust_net_info || uuu->adjust_net_info(uuu->net_info, uuu->adjust_data, uuu->failure_count) == 0) { if (read_mode != eRM_DropUnread && uuu->failure_count > 1) { CORE_LOGF_X(3, eLOG_Error, ("[HTTP] Retry attempts (%d) exhausted," " giving up", uuu->failure_count)); } uuu->can_connect = eCC_None; return 0/*failure*/; } ConnNetInfo_AdjustForHttpProxy(uuu->net_info); return 1/*success*/; }
extern NCBI_XCONNECT_EXPORT // FIXME: To remove once the API is fully official CConn_IOStream* NcbiOpenURL(const string& url, size_t buf_size) { { class CInPlaceConnIniter : protected CConnIniter { } conn_initer; } bool svc = x_IsIdentifier(url); AutoPtr<SConnNetInfo> net_info = ConnNetInfo_Create(svc ? url.c_str() : 0); if (svc) return new CConn_ServiceStream(url, fSERV_Any, net_info.get()); unsigned int host; unsigned short port; if (url.size() == CSocketAPI::StringToHostPort(url, &host, &port) && port && net_info.get()) { net_info->req_method = eReqMethod_Connect; } if (ConnNetInfo_ParseURL(net_info.get(), url.c_str())) { _ASSERT(net_info); // otherwise ConnNetInfo_ParseURL() would fail if (net_info->req_method == eReqMethod_Connect) { return new CConn_SocketStream(*net_info, 0, 0, fSOCK_LogDefault, net_info->timeout, buf_size); } switch (net_info->scheme) { case eURL_Https: case eURL_Http: return new CConn_HttpStream(net_info.get(), kEmptyStr, 0, 0, 0, 0, fHTTP_AutoReconnect, kDefaultTimeout, buf_size); case eURL_File: if (*net_info->host || net_info->port) break; /*not supported*/ _ASSERT(!*net_info->args); if (net_info->debug_printout) { net_info->req_method = eReqMethod_Any; net_info->firewall = 0; net_info->stateless = 0; net_info->lb_disable = 0; net_info->http_proxy_leak = 0; net_info->http_proxy_host[0] = '\0'; net_info->http_proxy_port = 0; net_info->http_proxy_user[0] = '\0'; net_info->http_proxy_pass[0] = '\0'; net_info->proxy_host[0] = '\0'; net_info->max_try = 0; net_info->timeout = kInfiniteTimeout; ConnNetInfo_SetUserHeader(net_info.get(), 0); if (net_info->http_referer) { free((void*) net_info->http_referer); net_info->http_referer = 0; } ConnNetInfo_Log(net_info.get(), eLOG_Note, CORE_GetLOG()); } return new CConn_FileStream(net_info->path); case eURL_Ftp: if (!net_info->user[0]) { strcpy(net_info->user, "ftp"); if (!net_info->pass[0]) strcpy(net_info->pass, "-none@"); } return new CConn_FTPDownloadStream(*net_info, 0, 0, 0, net_info->timeout, buf_size); default: break; } } return 0; }
static CConn_IOStream::TConn_Pair s_HttpConnectorBuilder(const SConnNetInfo* net_info, const char* url, const char* host, unsigned short port, const char* path, const char* args, const char* user_header, FHTTP_ParseHeader parse_header, void* user_data, FHTTP_Adjust adjust, FHTTP_Cleanup cleanup, THTTP_Flags flags, const STimeout* timeout) { size_t len; AutoPtr<SConnNetInfo> x_net_info(net_info ? ConnNetInfo_Clone(net_info) : ConnNetInfo_Create(0)); if (!x_net_info.get()) { NCBI_THROW(CIO_Exception, eUnknown, "CConn_HttpStream::CConn_HttpStream(): Out of memory"); } if (url && !ConnNetInfo_ParseURL(x_net_info.get(), url)) { NCBI_THROW(CIO_Exception, eInvalidArg, "CConn_HttpStream::CConn_HttpStream(): Bad URL"); } if (host) { if ((len = *host ? strlen(host) : 0) >= sizeof(x_net_info->host)) { NCBI_THROW(CIO_Exception, eInvalidArg, "CConn_HttpStream::CConn_HttpStream(): Host too long"); } memcpy(x_net_info->host, host, ++len); } if (port) x_net_info->port = port; if (path) { if ((len = *path ? strlen(path) : 0) >= sizeof(x_net_info->path)) { NCBI_THROW(CIO_Exception, eInvalidArg, "CConn_HttpStream::CConn_HttpStream(): Path too long"); } memcpy(x_net_info->path, path, ++len); } if (args) { if ((len = *args ? strlen(args) : 0) >= sizeof(x_net_info->args)) { NCBI_THROW(CIO_Exception, eInvalidArg, "CConn_HttpStream::CConn_HttpStream(): Args too long"); } memcpy(x_net_info->args, args, ++len); } if (user_header && *user_header) ConnNetInfo_OverrideUserHeader(x_net_info.get(), user_header); x_SetupUserAgent(x_net_info.get()); if (timeout != kDefaultTimeout) x_net_info->timeout = timeout; CONNECTOR c = HTTP_CreateConnectorEx(x_net_info.get(), flags, parse_header, user_data, adjust, cleanup); return CConn_IOStream::TConn_Pair(c, eIO_Unknown); }
static void TEST_ConnNetInfo(void) { size_t n; char* str; char buf[80]; SConnNetInfo* net_info; CORE_LOG(eLOG_Note, "ConnNetInfo test started"); net_info = ConnNetInfo_Create(0); assert(net_info); assert(ConnNetInfo_ParseURL(net_info, "ftp://*****:*****@host:8888/ro.t/p@th" "?arg/arg:arg@arg:arg/arg")); assert(net_info->scheme == eURL_Ftp); assert(strcmp(net_info->user, "user") == 0); assert(strcmp(net_info->pass, "pass") == 0); assert(strcmp(net_info->host, "host") == 0); assert( net_info->port == 8888); assert(strcmp(net_info->path, "/ro.t/p@th") == 0); assert(strcmp(net_info->args, "arg/arg:arg@arg:arg/arg") == 0); assert(ConnNetInfo_ParseURL(net_info, "https://www/path" "?arg:arg@arg#frag")); assert( net_info->scheme == eURL_Https); assert( *net_info->user == 0); assert( *net_info->pass == 0); assert(strcmp(net_info->host, "www") == 0); assert( net_info->port == 0); assert(strcmp(net_info->path, "/path") == 0); assert(strcmp(net_info->args, "arg:arg@arg#frag") == 0); assert(ConnNetInfo_ParseURL(net_info, "/path1?arg1#frag2")); assert(strcmp(net_info->args, "arg1#frag2") == 0); assert(ConnNetInfo_ParseURL(net_info, "path0/0")); assert(strcmp(net_info->path, "/path0/0") == 0); assert(strcmp(net_info->args, "#frag2") == 0); assert(ConnNetInfo_ParseURL(net_info, "#frag3")); assert(strcmp(net_info->path, "/path0/0") == 0); assert(strcmp(net_info->args, "#frag3") == 0); assert(ConnNetInfo_ParseURL(net_info, "path2")); assert(strcmp(net_info->path, "/path0/path2") == 0); assert(strcmp(net_info->args, "#frag3") == 0); assert(ConnNetInfo_ParseURL(net_info, "/path3?arg3")); assert(strcmp(net_info->path, "/path3") == 0); assert(strcmp(net_info->args, "arg3#frag3") == 0); strcpy(net_info->user, "user"); strcpy(net_info->pass, "pass"); str = ConnNetInfo_URL(net_info); assert(str); assert(strcmp(str, "https://www/path3?arg3#frag3") == 0); free(str); assert(ConnNetInfo_ParseURL(net_info, "path4/path5?arg4#")); assert(strcmp(net_info->user, "user") == 0); assert(strcmp(net_info->pass, "pass") == 0); assert(strcmp(net_info->path, "/path4/path5") == 0); assert(strcmp(net_info->args, "arg4") == 0); assert(ConnNetInfo_ParseURL(net_info, "../path6")); assert(strcmp(net_info->path, "/path4/../path6") == 0); assert(strcmp(net_info->args, "") == 0); ConnNetInfo_SetUserHeader(net_info, ""); str = UTIL_PrintableString(net_info->http_user_header, 0, buf, 0); printf("HTTP User Header after set:\n%s%s%s\n", "\"" + !str, str ? buf : "NULL", "\"" + !str); assert(!net_info->http_user_header && !str); ConnNetInfo_AppendUserHeader(net_info, "T0: V0\n" "T1:V1\r\n" "T2: V2\r\n" "T3: V3\n" "T4: V4\n" "T1: V6"); str = UTIL_PrintableString(net_info->http_user_header, 0, buf, 0); if (str) *str = '\0'; printf("HTTP User Header after append:\n%s%s%s\n", "\"" + !str, str ? buf : "NULL", "\"" + !str); assert(strcmp(net_info->http_user_header, "T0: V0\n" "T1:V1\r\n" "T2: V2\r\n" "T3: V3\n" "T4: V4\n" "T1: V6\r\n") == 0); ConnNetInfo_OverrideUserHeader(net_info, "T0\r\n" "T5: V5\n" "T1: \t \r\n" "T2:V2.1\r\n" "T3:V3\r\n" "T4: W4"); str = UTIL_PrintableString(net_info->http_user_header, 0, buf, 0); if (str) *str = '\0'; printf("HTTP User Header after override:\n%s%s%s\n", "\"" + !str, str ? buf : "NULL", "\"" + !str); assert(strcmp(net_info->http_user_header, "T0: V0\n" "T2:V2.1\r\n" "T3:V3\n" "T4: W4\r\n" "T5: V5\r\n") == 0); ConnNetInfo_ExtendUserHeader(net_info, "T0: V0\n" "T1:V1\r\n" "T2:V2\n" "T3: T3:V3\n" "T4:\n" "T5"); str = UTIL_PrintableString(net_info->http_user_header, 0, buf, 0); if (str) *str = '\0'; printf("HTTP User Header after extend:\n%s%s%s\n", "\"" + !str, str ? buf : "NULL", "\"" + !str); assert(strcmp(net_info->http_user_header, "T0: V0\n" "T2:V2.1 V2\r\n" "T3:V3 T3:V3\n" "T4: W4\r\n" "T5: V5\r\n" "T1:V1\r\n") == 0); ConnNetInfo_SetUserHeader(net_info, 0); str = UTIL_PrintableString(net_info->http_user_header, 0, buf, 0); if (str) *str = '\0'; printf("HTTP User Header after reset:\n%s%s%s\n", "\"" + !str, str ? buf : "NULL", "\"" + !str); assert(!net_info->http_user_header); for (n = 0; n < sizeof(net_info->args); n++) net_info->args[n] = "0123456789"[rand() % 10]; strncpy0(net_info->args, "a=b&b=c&c=d", sizeof(net_info->args) - 1); printf("HTTP Arg: \"%s\"\n", net_info->args); ConnNetInfo_PrependArg(net_info, "d=e", 0); ConnNetInfo_PrependArg(net_info, "e", "f"); printf("HTTP Arg after prepend: \"%s\"\n", net_info->args); ConnNetInfo_AppendArg(net_info, "f=g", 0); ConnNetInfo_AppendArg(net_info, "g", "h"); printf("HTTP Arg after append: \"%s\"\n", net_info->args); ConnNetInfo_PreOverrideArg(net_info, "a=z&b", "y"); ConnNetInfo_PreOverrideArg(net_info, "c", "x"); printf("HTTP Arg after pre-override: \"%s\"\n", net_info->args); ConnNetInfo_PostOverrideArg(net_info, "d=w&e", "v"); ConnNetInfo_PostOverrideArg(net_info, "f", "u"); printf("HTTP Arg after post-override: \"%s\"\n", net_info->args); ConnNetInfo_DeleteArg(net_info, "g"); ConnNetInfo_DeleteArg(net_info, "h=n"); printf("HTTP Arg after delete: \"%s\"\n", net_info->args); ConnNetInfo_DeleteAllArgs(net_info, "a=b&p=q&f=d"); printf("HTTP Arg after delete-all: \"%s\"\n", net_info->args); ConnNetInfo_LogEx(net_info, eLOG_Note, CORE_GetLOG()); ConnNetInfo_Destroy(net_info); CORE_LOG(eLOG_Note, "ConnNetInfo test completed"); }