00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include "ncbi_ansi_ext.h"
00037 #include "ncbi_comm.h"
00038 #include "ncbi_priv.h"
00039 #include <connect/ncbi_http_connector.h>
00040 #include <ctype.h>
00041 #include <errno.h>
00042 #include <stdlib.h>
00043
00044 #define NCBI_USE_ERRCODE_X Connect_HTTP
00045
00046
00047
00048
00049
00050
00051
00052
00053 typedef enum {
00054 eCC_None,
00055 eCC_Once,
00056 eCC_Unlimited
00057 } ECanConnect;
00058
00059 typedef unsigned EBCanConnect;
00060 typedef unsigned short TBHCC_Flags;
00061
00062 typedef enum {
00063 eRM_Regular = 0,
00064 eRM_DropUnread = 1,
00065 eRM_WaitCalled = 2
00066 } EReadMode;
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079 typedef struct {
00080 SConnNetInfo* net_info;
00081 FHttpParseHTTPHeader parse_http_hdr;
00082 FHttpAdjustNetInfo adjust_net_info;
00083 FHttpAdjustCleanup adjust_cleanup;
00084 void* adjust_data;
00085
00086 TBHCC_Flags flags;
00087 unsigned error_header:1;
00088 EBCanConnect can_connect:2;
00089 unsigned read_header:1;
00090 unsigned shut_down:1;
00091 unsigned auth_sent:1;
00092 unsigned reserved:7;
00093 unsigned minor_fault:3;
00094 unsigned short major_fault;
00095 unsigned short code;
00096
00097 SOCK sock;
00098 const STimeout* o_timeout;
00099 STimeout oo_timeout;
00100 const STimeout* w_timeout;
00101 STimeout ww_timeout;
00102
00103 BUF http;
00104 BUF r_buf;
00105 BUF w_buf;
00106 size_t w_len;
00107
00108 size_t expected;
00109 size_t received;
00110 } SHttpConnector;
00111
00112
00113
00114 static int s_MessageIssued = 0;
00115 static FHTTP_NcbiMessageHook s_MessageHook = 0;
00116
00117
00118 typedef enum {
00119 eRetry_None = 0,
00120 eRetry_Redirect,
00121 eRetry_Authenticate,
00122 eRetry_ProxyAuthenticate
00123 } ERetry;
00124
00125
00126 typedef struct {
00127 ERetry mode;
00128 const char* data;
00129 } SRetry;
00130
00131
00132
00133 static EIO_Status s_Adjust(SHttpConnector* uuu,
00134 const SRetry* retry,
00135 EReadMode read_mode)
00136 {
00137 EIO_Status status;
00138
00139 assert(!uuu->sock && uuu->can_connect != eCC_None);
00140
00141 if (retry && retry->mode && ++uuu->minor_fault < 3) {
00142 int fail;
00143
00144 switch (retry->mode) {
00145 case eRetry_Redirect:
00146 if (retry->data && retry->data[0] != '?') {
00147 if (uuu->net_info->req_method == eReqMethod_Get
00148 || (uuu->flags & fHCC_InsecureRedirect)
00149 || !uuu->w_len) {
00150 int secure = uuu->net_info->scheme == eURL_Https ? 1 : 0;
00151 *uuu->net_info->args = '\0';
00152 fail = !ConnNetInfo_ParseURL(uuu->net_info, retry->data);
00153 if (!fail && secure
00154 && uuu->net_info->scheme != eURL_Https
00155 && !(uuu->flags & fHCC_InsecureRedirect)) {
00156 fail = -1;
00157 }
00158 } else
00159 fail = -1;
00160 } else
00161 fail = 1;
00162 if (fail) {
00163 CORE_LOGF_X(2, eLOG_Error,
00164 ("[HTTP] %s redirect to %s%s%s",
00165 fail < 0 ? "Prohibited" : "Cannot",
00166 retry->data ? "\"" : "<",
00167 retry->data ? retry->data : "NULL",
00168 retry->data ? "\"" : ">"));
00169 status = eIO_Closed;
00170 } else
00171 status = eIO_Success;
00172 break;
00173 case eRetry_Authenticate:
00174 case eRetry_ProxyAuthenticate:
00175 fail = uuu->auth_sent ? 1 : -1;
00176 CORE_LOGF_X(3, eLOG_Error,
00177 ("[HTTP] %s %s %c%s%c",
00178 retry->mode == eRetry_Authenticate
00179 ? "Authorization" : "Proxy authorization",
00180 fail < 0 ? "not yet implemented" : "failed",
00181 "(<"[!retry->data],
00182 retry->data ? retry->data : "NULL",
00183 ")>"[!retry->data]));
00184 status = fail < 0 ? eIO_NotSupported : eIO_Closed;
00185 break;
00186 default:
00187 status = eIO_Unknown;
00188 assert(0);
00189 break;
00190 }
00191 if (status != eIO_Success) {
00192 uuu->can_connect = eCC_None;
00193 return status;
00194 }
00195 } else {
00196 const char* msg;
00197
00198 uuu->minor_fault = 0;
00199
00200 if (++uuu->major_fault >= uuu->net_info->max_try) {
00201 msg = read_mode != eRM_DropUnread && uuu->major_fault > 1
00202 ? "[HTTP] Too many failed attempts (%d), giving up" : "";
00203 } else if (!uuu->adjust_net_info
00204 || uuu->adjust_net_info(uuu->net_info,
00205 uuu->adjust_data,
00206 uuu->major_fault) == 0) {
00207 msg = read_mode != eRM_DropUnread && uuu->major_fault > 1
00208 ? "[HTTP] Retry attempts (%d) exhausted, giving up" : "";
00209 } else
00210 msg = 0;
00211 if (msg) {
00212 if (*msg) {
00213 CORE_LOGF_X(1, eLOG_Error,
00214 (msg, uuu->major_fault));
00215 }
00216 uuu->can_connect = eCC_None;
00217 return eIO_Closed;
00218 }
00219 }
00220
00221 ConnNetInfo_AdjustForHttpProxy(uuu->net_info);
00222 return eIO_Success;
00223 }
00224
00225
00226
00227 static void s_DropConnection(SHttpConnector* uuu, const STimeout* timeout)
00228 {
00229 assert(uuu->sock);
00230 BUF_Erase(uuu->http);
00231 if (uuu->read_header)
00232 SOCK_Abort(uuu->sock);
00233 else
00234 SOCK_SetTimeout(uuu->sock, eIO_Close, timeout);
00235 SOCK_Close(uuu->sock);
00236 uuu->sock = 0;
00237 }
00238
00239
00240
00241
00242
00243
00244
00245 static EIO_Status s_Connect(SHttpConnector* uuu,
00246 EReadMode read_mode)
00247 {
00248 EIO_Status status;
00249
00250 assert(!uuu->sock);
00251 if (uuu->can_connect == eCC_None) {
00252 if (read_mode == eRM_Regular)
00253 CORE_LOG_X(5, eLOG_Error, "[HTTP] Connector no longer usable");
00254 return eIO_Closed;
00255 }
00256
00257
00258 for (;;) {
00259 int reset_user_header = 0;
00260 char* http_user_header = 0;
00261 TSOCK_Flags flags;
00262
00263 uuu->w_len = BUF_Size(uuu->w_buf);
00264 if (uuu->net_info->http_user_header)
00265 http_user_header = strdup(uuu->net_info->http_user_header);
00266 if (!uuu->net_info->http_user_header == !http_user_header) {
00267 ConnNetInfo_ExtendUserHeader
00268 (uuu->net_info, "User-Agent: NCBIHttpConnector"
00269 #ifdef NCBI_CXX_TOOLKIT
00270 " (C++ Toolkit)"
00271 #else
00272 " (C Toolkit)"
00273 #endif
00274 "\r\n");
00275 reset_user_header = 1;
00276 }
00277 if (uuu->net_info->debug_printout)
00278 ConnNetInfo_Log(uuu->net_info, CORE_GetLOG());
00279 flags = (uuu->net_info->debug_printout == eDebugPrintout_Data
00280 ? fSOCK_LogOn : fSOCK_LogDefault);
00281 if (uuu->net_info->scheme == eURL_Https)
00282 flags |= fSOCK_Secure;
00283 if (!(uuu->flags & fHCC_NoUpread))
00284 flags |= fSOCK_ReadOnWrite;
00285
00286 if ((status = URL_ConnectEx
00287 (uuu->net_info->host, uuu->net_info->port,
00288 uuu->net_info->path, uuu->net_info->args,
00289 uuu->net_info->req_method, uuu->w_len,
00290 uuu->o_timeout, uuu->w_timeout,
00291 uuu->net_info->http_user_header,
00292 (int)(uuu->flags & fHCC_UrlEncodeArgs),
00293 flags, &uuu->sock)) != eIO_Success) {
00294 uuu->sock = 0;
00295 }
00296 if (reset_user_header) {
00297 ConnNetInfo_SetUserHeader(uuu->net_info, 0);
00298 uuu->net_info->http_user_header = http_user_header;
00299 }
00300 if (uuu->sock)
00301 break;
00302
00303
00304 if (s_Adjust(uuu, 0, read_mode) != eIO_Success)
00305 break;
00306 }
00307
00308 return status;
00309 }
00310
00311
00312
00313
00314
00315
00316
00317 static EIO_Status s_ConnectAndSend(SHttpConnector* uuu,
00318 EReadMode read_mode)
00319 {
00320 EIO_Status status;
00321
00322 for (;;) {
00323 size_t body_size;
00324 char buf[4096];
00325
00326 if (!uuu->sock) {
00327 if ((status = s_Connect(uuu, read_mode)) != eIO_Success)
00328 break;
00329 assert(uuu->sock);
00330 uuu->read_header = 1;
00331 uuu->shut_down = 0;
00332 uuu->expected = 0;
00333 uuu->received = 0;
00334 uuu->code = 0;
00335 } else
00336 status = eIO_Success;
00337
00338 if (uuu->w_len) {
00339 size_t off = BUF_Size(uuu->w_buf) - uuu->w_len;
00340
00341 SOCK_SetTimeout(uuu->sock, eIO_Write, uuu->w_timeout);
00342 do {
00343 size_t n_written;
00344 size_t n_write = BUF_PeekAt(uuu->w_buf, off, buf, sizeof(buf));
00345 status = SOCK_Write(uuu->sock, buf, n_write,
00346 &n_written, eIO_WritePlain);
00347 if (status != eIO_Success)
00348 break;
00349 uuu->w_len -= n_written;
00350 off += n_written;
00351 } while (uuu->w_len);
00352 } else if (!uuu->shut_down)
00353 status = SOCK_Write(uuu->sock, 0, 0, 0, eIO_WritePlain);
00354
00355 if (status == eIO_Success) {
00356 assert(uuu->w_len == 0);
00357 if (!uuu->shut_down) {
00358
00359
00360
00361
00362
00363
00364
00365 uuu->shut_down = 1;
00366 }
00367 break;
00368 }
00369
00370 if (status == eIO_Timeout
00371 && (read_mode == eRM_WaitCalled
00372 || (uuu->w_timeout
00373 && !(uuu->w_timeout->sec | uuu->w_timeout->usec)))) {
00374 break;
00375 }
00376 if ((body_size = BUF_Size(uuu->w_buf))) {
00377 sprintf(buf, "writing request body at offset %lu",
00378 (unsigned long)(body_size - uuu->w_len));
00379 } else
00380 strcpy(buf, "flushing request header");
00381 CORE_LOGF_X(6, eLOG_Error,
00382 ("[HTTP] Error %s (%s)", buf, IO_StatusStr(status)));
00383
00384
00385 s_DropConnection(uuu, 0);
00386 if ((status = s_Adjust(uuu, 0, read_mode)) != eIO_Success)
00387 break;
00388 }
00389
00390 return status;
00391 }
00392
00393
00394 static int s_IsValidParam(const char* param, size_t parlen)
00395 {
00396 const char* e = (const char*) memchr(param, '=', parlen);
00397 size_t toklen;
00398 if (!e || e == param)
00399 return 0;
00400 if ((toklen = (size_t)(++e - param)) >= parlen)
00401 return 0;
00402 assert(!isspace((unsigned char)(*param)));
00403 if (strcspn(param, " \t") < toklen)
00404 return 0;
00405 if (*e == '\'' || *e == '"') {
00406
00407 toklen = parlen - toklen;
00408 if (!(e = (const char*) memchr(e + 1, *e, --toklen)))
00409 return 0;
00410 e++;
00411 } else
00412 e += strcspn(e, " \t");
00413 if (e != param + parlen && e + strspn(e, " \t") != param + parlen)
00414 return 0;
00415 return 1;
00416 }
00417
00418
00419 static int s_IsValidAuth(const char* challenge, size_t len)
00420 {
00421
00422
00423
00424
00425 size_t word = strcspn(challenge, " \t");
00426 int retval = 0;
00427 if (word < len) {
00428
00429 const char* param = challenge + word;
00430 for (param += strspn(param, " \t"); param < challenge + len;
00431 param += strspn(param, ", \t")) {
00432 size_t parlen = (size_t)(challenge + len - param);
00433 const char* c = (const char*) memchr(param, ',', parlen);
00434 if (c)
00435 parlen = (size_t)(c - param);
00436 if (!s_IsValidParam(param, parlen))
00437 return 0;
00438 if (parlen > 6 && strncasecmp(param, "realm=", 6) == 0)
00439 retval = 1;
00440 param += c ? ++parlen : parlen;
00441 }
00442 }
00443 return retval;
00444 }
00445
00446
00447
00448 static EIO_Status s_ReadHeader(SHttpConnector* uuu,
00449 SRetry* retry,
00450 EReadMode read_mode)
00451 {
00452 int server_error = 0;
00453 int http_status;
00454 EIO_Status status;
00455 char* header;
00456 size_t size;
00457
00458 assert(uuu->sock && uuu->read_header);
00459 retry->mode = eRetry_None;
00460 retry->data = 0;
00461
00462
00463 for (;;) {
00464
00465 size = BUF_Size(uuu->http);
00466 if (!(header = (char*) malloc(size + 1))) {
00467 CORE_LOGF_X(7, eLOG_Error,
00468 ("[HTTP] Cannot allocate header, %lu bytes",
00469 (unsigned long) size));
00470 return eIO_Unknown;
00471 }
00472 verify(BUF_Peek(uuu->http, header, size) == size);
00473 header[size] = '\0';
00474 if (size >= 4 && strcmp(&header[size - 4], "\r\n\r\n") == 0)
00475 break;
00476 free(header);
00477
00478 status = SOCK_StripToPattern(uuu->sock, "\r\n", 2, &uuu->http, 0);
00479 if (status != eIO_Success) {
00480 ELOG_Level level;
00481 if (status == eIO_Timeout) {
00482 const STimeout* tmo = SOCK_GetTimeout(uuu->sock, eIO_Read);
00483 if (!tmo)
00484 level = eLOG_Error;
00485 else if (read_mode == eRM_WaitCalled)
00486 return status;
00487 else if (tmo->sec | tmo->usec)
00488 level = eLOG_Warning;
00489 else
00490 level = eLOG_Trace;
00491 } else
00492 level = eLOG_Error;
00493 CORE_LOGF_X(8, level, ("[HTTP] Error reading header (%s)",
00494 IO_StatusStr(status)));
00495 return status;
00496 }
00497 }
00498
00499 uuu->read_header = 0;
00500 status = eIO_Success;
00501 BUF_Erase(uuu->http);
00502
00503
00504 if (sscanf(header, "HTTP/%*d.%*d %d ", &http_status) != 1 || !http_status)
00505 http_status = -1;
00506 if (http_status < 200 || 299 < http_status) {
00507 server_error = http_status;
00508 if (http_status == 301 || http_status == 302 || http_status == 307)
00509 retry->mode = eRetry_Redirect;
00510 else if (http_status == 401)
00511 retry->mode = eRetry_Authenticate;
00512 else if (http_status == 407)
00513 retry->mode = eRetry_ProxyAuthenticate;
00514 else if (http_status < 0 || http_status == 403 || http_status == 404)
00515 uuu->net_info->max_try = 0;
00516 }
00517 uuu->code = http_status < 0 ? -1 : http_status;
00518
00519 if ((server_error || !uuu->error_header)
00520 && uuu->net_info->debug_printout == eDebugPrintout_Some) {
00521
00522
00523 const char* header_header;
00524 if (!server_error)
00525 header_header = "HTTP header";
00526 else if (uuu->flags & fHCC_KeepHeader)
00527 header_header = "HTTP header (error)";
00528 else if (uuu->code == 301 || uuu->code == 302 || uuu->code == 307)
00529 header_header = "HTTP header (moved)";
00530 else if (!uuu->net_info->max_try)
00531 header_header = "HTTP header (unrecoverable error)";
00532 else
00533 header_header = "HTTP header (server error, can retry)";
00534 CORE_DATA_X(19, header, size, header_header);
00535 }
00536
00537 {{
00538
00539 static const char kNcbiMessageTag[] = "\n" HTTP_NCBI_MESSAGE " ";
00540 const char* s;
00541 for (s = strchr(header, '\n'); s && *s; s = strchr(s + 1, '\n')) {
00542 if (strncasecmp(s, kNcbiMessageTag, sizeof(kNcbiMessageTag)-1)==0){
00543 const char* message = s + sizeof(kNcbiMessageTag) - 1;
00544 while (*message && isspace((unsigned char)(*message)))
00545 message++;
00546 if (!(s = strchr(message, '\r')))
00547 s = strchr(message, '\n');
00548 assert(s);
00549 do {
00550 if (!isspace((unsigned char) s[-1]))
00551 break;
00552 } while (--s > message);
00553 if (message != s) {
00554 if (s_MessageHook) {
00555 if (s_MessageIssued <= 0) {
00556 s_MessageIssued = 1;
00557 s_MessageHook(message);
00558 }
00559 } else {
00560 s_MessageIssued = -1;
00561 CORE_LOGF_X(10, eLOG_Critical,
00562 ("[NCBI-MESSAGE] %.*s",
00563 (int)(s - message), message));
00564 }
00565 }
00566 break;
00567 }
00568 }
00569 }}
00570
00571 if (uuu->flags & fHCC_KeepHeader) {
00572 if (!BUF_Write(&uuu->r_buf, header, size)) {
00573 CORE_LOG_X(11, eLOG_Error, "[HTTP] Cannot keep HTTP header");
00574 status = eIO_Unknown;
00575 }
00576 free(header);
00577 return status;
00578 }
00579
00580 if (uuu->parse_http_hdr
00581 && !(*uuu->parse_http_hdr)(header, uuu->adjust_data, server_error)) {
00582 server_error = 1;
00583 }
00584
00585 if (retry->mode == eRetry_Redirect) {
00586
00587 static const char kLocationTag[] = "\nLocation: ";
00588 char* s;
00589 for (s = strchr(header, '\n'); s && *s; s = strchr(s + 1, '\n')) {
00590 if (strncasecmp(s, kLocationTag, sizeof(kLocationTag) - 1) == 0) {
00591 char* location = s + sizeof(kLocationTag) - 1;
00592 while (*location && isspace((unsigned char)(*location)))
00593 location++;
00594 if (!(s = strchr(location, '\r')))
00595 s = strchr(location, '\n');
00596 if (!s)
00597 break;
00598 do {
00599 if (!isspace((unsigned char) s[-1]))
00600 break;
00601 } while (--s > location);
00602 if (s != location) {
00603 size_t len = (size_t)(s - location);
00604 memmove(header, location, len);
00605 header[len] = '\0';
00606 retry->data = header;
00607 }
00608 break;
00609 }
00610 }
00611 } else if (retry->mode != eRetry_None) {
00612
00613 static const char kAuthenticateTag[] = "-Authenticate: ";
00614 char* s;
00615 for (s = strchr(header, '\n'); s && *s; s = strchr(s + 1, '\n')) {
00616 if (strncasecmp(s + (retry->mode == eRetry_Authenticate ? 4 : 6),
00617 kAuthenticateTag, sizeof(kAuthenticateTag)-1)==0){
00618 if ((retry->mode == eRetry_Authenticate
00619 && strncasecmp(s, "\nWWW", 4) == 0) ||
00620 (retry->mode == eRetry_ProxyAuthenticate
00621 && strncasecmp(s, "\nProxy", 6) == 0)) {
00622 char* challenge = s + sizeof(kAuthenticateTag) - 1, *e;
00623 challenge += retry->mode == eRetry_Authenticate ? 4 : 6;
00624 while (*challenge && isspace((unsigned char)(*challenge)))
00625 challenge++;
00626 if (!(e = strchr(challenge, '\r')))
00627 e = strchr(challenge, '\n');
00628 else
00629 s = e;
00630 if (!e)
00631 break;
00632 do {
00633 if (!isspace((unsigned char) e[-1]))
00634 break;
00635 } while (--e > challenge);
00636 if (e != challenge) {
00637 size_t len = (size_t)(e - challenge);
00638 if (s_IsValidAuth(challenge, len)) {
00639 memmove(header, challenge, len);
00640 header[len] = '\0';
00641 retry->data = header;
00642 break;
00643 }
00644 }
00645 }
00646 }
00647 }
00648 } else if (!server_error) {
00649 static const char kContentLengthTag[] = "\nContent-Length: ";
00650 const char* s;
00651 for (s = strchr(header, '\n'); s && *s; s = strchr(s + 1, '\n')) {
00652 if (!strncasecmp(s,kContentLengthTag,sizeof(kContentLengthTag)-1)){
00653 const char* expected = s + sizeof(kContentLengthTag) - 1;
00654 while (*expected && isspace((unsigned char)(*expected)))
00655 expected++;
00656 if (!(s = strchr(expected, '\r')))
00657 s = strchr(expected, '\n');
00658 assert(s);
00659 do {
00660 if (!isspace((unsigned char) s[-1]))
00661 break;
00662 } while (--s > expected);
00663 if (s != expected) {
00664 char* e;
00665 errno = 0;
00666 uuu->expected = (size_t) strtol(expected, &e, 10);
00667 if (errno || e != s)
00668 uuu->expected = 0;
00669 else if (!uuu->expected)
00670 uuu->expected = (size_t)(-1L);
00671 }
00672 break;
00673 }
00674 }
00675 }
00676 if (!retry->data)
00677 free(header);
00678
00679
00680 if (server_error && uuu->net_info->debug_printout == eDebugPrintout_Some) {
00681 BUF buf = 0;
00682 char* body;
00683
00684 SOCK_SetTimeout(uuu->sock, eIO_Read, 0);
00685
00686 SOCK_StripToPattern(uuu->sock, 0, 0, &buf, 0);
00687 if (!(size = BUF_Size(buf))) {
00688 CORE_LOG_X(12, eLOG_Trace,
00689 "[HTTP] No HTTP body received with this error");
00690 } else if ((body = (char*) malloc(size)) != 0) {
00691 size_t n = BUF_Read(buf, body, size);
00692 if (n != size) {
00693 CORE_LOGF_X(13, eLOG_Error,
00694 ("[HTTP] Cannot read server error "
00695 "body from buffer (%lu out of %lu)",
00696 (unsigned long) n, (unsigned long) size));
00697 }
00698 CORE_DATA_X(20, body, n, "Server error body");
00699 free(body);
00700 } else {
00701 CORE_LOGF_X(14, eLOG_Error,
00702 ("[HTTP] Cannot allocate server error "
00703 "body, %lu bytes", (unsigned long) size));
00704 }
00705 BUF_Destroy(buf);
00706 }
00707
00708 return server_error ? eIO_Unknown : status;
00709 }
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719 static EIO_Status s_PreRead(SHttpConnector* uuu,
00720 const STimeout* timeout,
00721 EReadMode read_mode)
00722 {
00723 EIO_Status status;
00724
00725 for (;;) {
00726 EIO_Status adjust_status;
00727 SRetry retry;
00728
00729 status = s_ConnectAndSend(uuu, read_mode);
00730 if (!uuu->sock) {
00731 assert(status != eIO_Success);
00732 break;
00733 }
00734 if (status != eIO_Success) {
00735 if (status != eIO_Timeout ||
00736 status == SOCK_Status(uuu->sock, eIO_Read))
00737 break;
00738 }
00739
00740
00741 verify(SOCK_SetTimeout(uuu->sock, eIO_Read, timeout) == eIO_Success);
00742
00743 if (!uuu->read_header)
00744 break;
00745
00746 if ((status = s_ReadHeader(uuu, &retry, read_mode)) == eIO_Success) {
00747
00748 assert(!uuu->read_header && !retry.mode);
00749 BUF_Erase(uuu->w_buf);
00750 break;
00751 }
00752 assert(status != eIO_Timeout || !retry.mode);
00753
00754 if (status == eIO_Timeout
00755 && (read_mode == eRM_WaitCalled
00756 || (timeout && !(timeout->sec | timeout->usec)))) {
00757 assert(!retry.data);
00758 break;
00759 }
00760
00761
00762 s_DropConnection(uuu, 0);
00763 adjust_status = s_Adjust(uuu, retry.mode ? &retry : 0, read_mode);
00764 if (retry.data)
00765 free((void*) retry.data);
00766 if (adjust_status != eIO_Success) {
00767 if (adjust_status != eIO_Closed)
00768 status = adjust_status;
00769 break;
00770 }
00771 }
00772
00773 return status;
00774 }
00775
00776
00777
00778 static EIO_Status s_Read(SHttpConnector* uuu, void* buf,
00779 size_t size, size_t* n_read)
00780 {
00781 EIO_Status status;
00782
00783 assert(uuu->sock);
00784 if (uuu->flags & fHCC_UrlDecodeInput) {
00785
00786 size_t n_peeked, n_decoded;
00787 size_t peek_size = 3 * size;
00788 void* peek_buf = malloc(peek_size);
00789
00790
00791 status= SOCK_Read(uuu->sock,peek_buf,peek_size,&n_peeked,eIO_ReadPeek);
00792 if (status != eIO_Success) {
00793 assert(!n_peeked);
00794 *n_read = 0;
00795 } else {
00796 if (URL_Decode(peek_buf,n_peeked,&n_decoded,buf,size,n_read)) {
00797
00798 if (n_decoded) {
00799 SOCK_Read(uuu->sock,0,n_decoded,&n_peeked,eIO_ReadPersist);
00800 assert(n_peeked == n_decoded);
00801 uuu->received += n_decoded;
00802 status = eIO_Success;
00803 } else if (SOCK_Status(uuu->sock, eIO_Read) == eIO_Closed) {
00804
00805 status = eIO_Unknown;
00806 }
00807 } else
00808 status = eIO_Unknown;
00809
00810 if (status != eIO_Success)
00811 CORE_LOG_X(16, eLOG_Error, "[HTTP] Cannot URL-decode data");
00812 }
00813 free(peek_buf);
00814 } else {
00815
00816 status = SOCK_Read(uuu->sock, buf, size, n_read, eIO_ReadPlain);
00817 uuu->received += *n_read;
00818 }
00819
00820 if (uuu->expected) {
00821 if (uuu->received > uuu->expected)
00822 return eIO_Unknown;
00823 if (uuu->expected != (size_t)(-1L)) {
00824 if (status == eIO_Closed && uuu->expected > uuu->received)
00825 return eIO_Unknown;
00826 } else if (uuu->received)
00827 return eIO_Unknown;
00828 }
00829 return status;
00830 }
00831
00832
00833
00834 static EIO_Status s_Disconnect(SHttpConnector* uuu,
00835 const STimeout* timeout,
00836 EReadMode read_mode)
00837 {
00838 EIO_Status status = eIO_Success;
00839
00840 if (read_mode == eRM_DropUnread)
00841 BUF_Erase(uuu->r_buf);
00842 else if ((status = s_PreRead(uuu, timeout, read_mode)) == eIO_Success) {
00843 do {
00844 char buf[4096];
00845 size_t x_read;
00846 status = s_Read(uuu, buf, sizeof(buf), &x_read);
00847 if (!BUF_Write(&uuu->r_buf, buf, x_read))
00848 status = eIO_Unknown;
00849 } while (status == eIO_Success);
00850 if (status == eIO_Closed)
00851 status = eIO_Success;
00852 }
00853
00854 if (uuu->sock)
00855 s_DropConnection(uuu, timeout);
00856 if (uuu->can_connect == eCC_Once)
00857 uuu->can_connect = eCC_None;
00858
00859 return status;
00860 }
00861
00862
00863
00864
00865
00866
00867 static void s_FlushAndDisconnect(SHttpConnector* uuu,
00868 int close,
00869 const STimeout* timeout)
00870 {
00871
00872 if (timeout) {
00873 uuu->oo_timeout = *timeout;
00874 uuu->o_timeout = &uuu->oo_timeout;
00875 uuu->ww_timeout = *timeout;
00876 uuu->w_timeout = &uuu->ww_timeout;
00877 } else {
00878 uuu->o_timeout = timeout;
00879 uuu->w_timeout = timeout;
00880 }
00881
00882 if (close && uuu->can_connect != eCC_None && !uuu->sock
00883 && ((uuu->flags & fHCC_SureFlush) || BUF_Size(uuu->w_buf))) {
00884
00885 s_PreRead(uuu, timeout, eRM_DropUnread);
00886 }
00887 s_Disconnect(uuu, timeout, eRM_DropUnread);
00888 assert(!uuu->sock);
00889
00890
00891 BUF_Erase(uuu->w_buf);
00892 }
00893
00894
00895
00896
00897
00898
00899 #ifdef __cplusplus
00900 extern "C" {
00901 #endif
00902 static const char* s_VT_GetType (CONNECTOR connector);
00903 static char* s_VT_Descr (CONNECTOR connector);
00904 static EIO_Status s_VT_Open (CONNECTOR connector,
00905 const STimeout* timeout);
00906 static EIO_Status s_VT_Wait (CONNECTOR connector,
00907 EIO_Event event,
00908 const STimeout* timeout);
00909 static EIO_Status s_VT_Write (CONNECTOR connector,
00910 const void* buf,
00911 size_t size,
00912 size_t* n_written,
00913 const STimeout* timeout);
00914 static EIO_Status s_VT_Flush (CONNECTOR connector,
00915 const STimeout* timeout);
00916 static EIO_Status s_VT_Read (CONNECTOR connector,
00917 void* buf,
00918 size_t size,
00919 size_t* n_read,
00920 const STimeout* timeout);
00921 static EIO_Status s_VT_Status (CONNECTOR connector,
00922 EIO_Event dir);
00923 static EIO_Status s_VT_Close (CONNECTOR connector,
00924 const STimeout* timeout);
00925
00926 static void s_Setup (SMetaConnector *meta,
00927 CONNECTOR connector);
00928 static void s_Destroy (CONNECTOR connector);
00929 # ifdef IMPLEMENTED__CONN_WaitAsync
00930 static EIO_Status s_VT_WaitAsync(CONNECTOR connector,
00931 FConnectorAsyncHandler func,
00932 SConnectorAsyncHandler* data);
00933 # endif
00934 #ifdef __cplusplus
00935 }
00936 #endif
00937
00938
00939
00940 static const char* s_VT_GetType
00941 (CONNECTOR connector)
00942 {
00943 return "HTTP";
00944 }
00945
00946
00947 static char* s_VT_Descr
00948 (CONNECTOR connector)
00949 {
00950 return ConnNetInfo_URL(((SHttpConnector*) connector->handle)->net_info);
00951 }
00952
00953
00954 static EIO_Status s_VT_Open
00955 (CONNECTOR connector,
00956 const STimeout* timeout)
00957 {
00958 SHttpConnector* uuu = (SHttpConnector*) connector->handle;
00959
00960
00961
00962
00963 s_FlushAndDisconnect(uuu, 0, timeout);
00964
00965
00966 uuu->can_connect = uuu->flags & fHCC_AutoReconnect
00967 ? eCC_Unlimited : eCC_Once;
00968 uuu->major_fault = 0;
00969 uuu->minor_fault = 0;
00970 uuu->auth_sent = 0;
00971
00972 return eIO_Success;
00973 }
00974
00975
00976 static EIO_Status s_VT_Wait
00977 (CONNECTOR connector,
00978 EIO_Event event,
00979 const STimeout* timeout)
00980 {
00981 SHttpConnector* uuu = (SHttpConnector*) connector->handle;
00982 EIO_Status status;
00983
00984 switch (event) {
00985 case eIO_Read:
00986 if (BUF_Size(uuu->r_buf))
00987 return eIO_Success;
00988 if (uuu->can_connect == eCC_None)
00989 return eIO_Closed;
00990 if ((status = s_PreRead(uuu, timeout, eRM_WaitCalled)) != eIO_Success)
00991 return status;
00992 assert(uuu->sock);
00993 return SOCK_Wait(uuu->sock, eIO_Read, timeout);
00994 case eIO_Write:
00995
00996 return uuu->can_connect == eCC_None
00997 || (uuu->sock && uuu->can_connect == eCC_Once)
00998 ? eIO_Closed : eIO_Success;
00999 default:
01000 break;
01001 }
01002 assert(0);
01003 return eIO_InvalidArg;
01004 }
01005
01006
01007 static EIO_Status s_VT_Write
01008 (CONNECTOR connector,
01009 const void* buf,
01010 size_t size,
01011 size_t* n_written,
01012 const STimeout* timeout)
01013 {
01014 SHttpConnector* uuu = (SHttpConnector*) connector->handle;
01015
01016
01017
01018 if (uuu->sock) {
01019 EIO_Status status = s_Disconnect(uuu, timeout,
01020 uuu->flags & fHCC_DropUnread
01021 ? eRM_DropUnread : eRM_Regular);
01022 if (status != eIO_Success)
01023 return status;
01024 }
01025 if (uuu->can_connect == eCC_None)
01026 return eIO_Closed;
01027
01028
01029 if (uuu->flags & fHCC_UrlEncodeOutput) {
01030
01031 size_t dst_size = 3 * size;
01032 void* dst = malloc(dst_size);
01033 size_t dst_written;
01034 URL_Encode(buf, size, n_written, dst, dst_size, &dst_written);
01035 assert(*n_written == size);
01036 if (!BUF_Write(&uuu->w_buf, dst, dst_written)) {
01037 free(dst);
01038 return eIO_Unknown;
01039 }
01040 free(dst);
01041 } else {
01042
01043 if (!BUF_Write(&uuu->w_buf, buf, size))
01044 return eIO_Unknown;
01045 *n_written = size;
01046 }
01047
01048
01049 if (timeout) {
01050 uuu->ww_timeout = *timeout;
01051 uuu->w_timeout = &uuu->ww_timeout;
01052 } else
01053 uuu->w_timeout = timeout;
01054
01055 return eIO_Success;
01056 }
01057
01058
01059 static EIO_Status s_VT_Flush
01060 (CONNECTOR connector,
01061 const STimeout* timeout)
01062 {
01063 SHttpConnector* uuu = (SHttpConnector*) connector->handle;
01064
01065 assert(connector->meta);
01066 if (!(uuu->flags & fHCC_Flushable) || uuu->sock) {
01067
01068
01069
01070
01071 if (timeout) {
01072 uuu->ww_timeout = *timeout;
01073 uuu->w_timeout = &uuu->ww_timeout;
01074 } else
01075 uuu->w_timeout = timeout;
01076
01077 return eIO_Success;
01078 }
01079 if (!connector->meta->wait)
01080 return eIO_NotSupported;
01081
01082 return connector->meta->wait(connector->meta->c_wait, eIO_Read, timeout);
01083 }
01084
01085
01086 static EIO_Status s_VT_Read
01087 (CONNECTOR connector,
01088 void* buf,
01089 size_t size,
01090 size_t* n_read,
01091 const STimeout* timeout)
01092 {
01093 SHttpConnector* uuu = (SHttpConnector*) connector->handle;
01094 EIO_Status status = s_PreRead(uuu, timeout, eRM_Regular);
01095 size_t x_read = BUF_Read(uuu->r_buf, buf, size);
01096
01097 *n_read = x_read;
01098 if (x_read < size) {
01099 if (status != eIO_Success)
01100 return status;
01101 status = s_Read(uuu, (char*) buf + x_read, size - x_read, n_read);
01102 *n_read += x_read;
01103 } else
01104 status = eIO_Success;
01105
01106 return *n_read ? eIO_Success : status;
01107 }
01108
01109
01110 static EIO_Status s_VT_Status
01111 (CONNECTOR connector,
01112 EIO_Event dir)
01113 {
01114 SHttpConnector* uuu = (SHttpConnector*) connector->handle;
01115 return uuu->sock ? SOCK_Status(uuu->sock, dir) :
01116 (uuu->can_connect == eCC_None ? eIO_Closed : eIO_Success);
01117 }
01118
01119
01120 static EIO_Status s_VT_Close
01121 (CONNECTOR connector,
01122 const STimeout* timeout)
01123 {
01124 s_FlushAndDisconnect((SHttpConnector*) connector->handle,
01125 1, timeout);
01126 return eIO_Success;
01127 }
01128
01129
01130 #ifdef IMPLEMENTED__CONN_WaitAsync
01131 static EIO_Status s_VT_WaitAsync
01132 (void* connector,
01133 FConnectorAsyncHandler func,
01134 SConnectorAsyncHandler* data)
01135 {
01136 return eIO_NotSupported;
01137 }
01138 #endif
01139
01140
01141 static void s_Setup(SMetaConnector *meta, CONNECTOR connector)
01142 {
01143 SHttpConnector* uuu = (SHttpConnector*) connector->handle;
01144
01145
01146 CONN_SET_METHOD(meta, get_type, s_VT_GetType, connector);
01147 CONN_SET_METHOD(meta, descr, s_VT_Descr, connector);
01148 CONN_SET_METHOD(meta, open, s_VT_Open, connector);
01149 CONN_SET_METHOD(meta, wait, s_VT_Wait, connector);
01150 CONN_SET_METHOD(meta, write, s_VT_Write, connector);
01151 CONN_SET_METHOD(meta, flush, s_VT_Flush, connector);
01152 CONN_SET_METHOD(meta, read, s_VT_Read, connector);
01153 CONN_SET_METHOD(meta, status, s_VT_Status, connector);
01154 CONN_SET_METHOD(meta, close, s_VT_Close, connector);
01155 #ifdef IMPLEMENTED__CONN_WaitAsync
01156 CONN_SET_METHOD(meta, wait_async, s_VT_WaitAsync, connector);
01157 #endif
01158 CONN_SET_DEFAULT_TIMEOUT(meta, uuu->net_info->timeout);
01159 }
01160
01161
01162 static void s_Destroy(CONNECTOR connector)
01163 {
01164 SHttpConnector* uuu = (SHttpConnector*) connector->handle;
01165
01166 if (uuu->adjust_cleanup)
01167 uuu->adjust_cleanup(uuu->adjust_data);
01168 ConnNetInfo_Destroy(uuu->net_info);
01169 BUF_Destroy(uuu->http);
01170 BUF_Destroy(uuu->r_buf);
01171 BUF_Destroy(uuu->w_buf);
01172 free(uuu);
01173 connector->handle = 0;
01174 free(connector);
01175 }
01176
01177
01178
01179 static void s_AddReferer(SConnNetInfo* net_info)
01180 {
01181 const char* s;
01182 char* referer;
01183 if (!net_info || !net_info->http_referer || !*net_info->http_referer)
01184 return;
01185 if ((s = net_info->http_user_header) != 0) {
01186 int first;
01187 for (first = 1; *s; first = 0) {
01188 if (strncasecmp(s++, "\nReferer: " + first, 10 - first) == 0)
01189 return;
01190 if (!(s = strchr(s, '\n')))
01191 break;
01192 }
01193 }
01194 if (!(referer = (char*) malloc(strlen(net_info->http_referer) + 12)))
01195 return;
01196 sprintf(referer, "Referer: %s\r\n", net_info->http_referer);
01197 ConnNetInfo_ExtendUserHeader(net_info, referer);
01198 free(referer);
01199 }
01200
01201
01202 static CONNECTOR s_CreateConnector
01203 (const SConnNetInfo* net_info,
01204 const char* user_header,
01205 THCC_Flags flags,
01206 FHttpParseHTTPHeader parse_http_hdr,
01207 FHttpAdjustNetInfo adjust_net_info,
01208 void* adjust_data,
01209 FHttpAdjustCleanup adjust_cleanup)
01210 {
01211 char value[32];
01212 CONNECTOR ccc;
01213 SHttpConnector* uuu;
01214 SConnNetInfo* xxx;
01215 char* fff;
01216
01217 xxx = net_info ? ConnNetInfo_Clone(net_info) : ConnNetInfo_Create(0);
01218 if (!xxx)
01219 return 0;
01220 if ((flags & fHCC_NoAutoRetry) || !xxx->max_try)
01221 xxx->max_try = 1;
01222
01223 if ((fff = strchr(xxx->args, '#')) != 0)
01224 *fff = '\0';
01225 if ((xxx->scheme != eURL_Unspec &&
01226 xxx->scheme != eURL_Https &&
01227 xxx->scheme != eURL_Http) ||
01228 !ConnNetInfo_AdjustForHttpProxy(xxx)) {
01229 ConnNetInfo_Destroy(xxx);
01230 return 0;
01231 }
01232 if (xxx->scheme == eURL_Unspec)
01233 xxx->scheme = eURL_Http;
01234
01235 if (!(ccc = (SConnector *) malloc(sizeof(SConnector ))) ||
01236 !(uuu = (SHttpConnector*) malloc(sizeof(SHttpConnector)))) {
01237 if (ccc)
01238 free(ccc);
01239 ConnNetInfo_Destroy(xxx);
01240 return 0;
01241 }
01242 if (user_header)
01243 ConnNetInfo_OverrideUserHeader(xxx, user_header);
01244 s_AddReferer(xxx);
01245
01246 ConnNetInfo_GetValue(0, "HTTP_INSECURE_REDIRECT", value, sizeof(value),"");
01247 if (*value && (strcmp (value, "1") == 0 ||
01248 strcasecmp(value, "on") == 0 ||
01249 strcasecmp(value, "yes") == 0 ||
01250 strcasecmp(value, "true") == 0)) {
01251 flags |= fHCC_InsecureRedirect;
01252 }
01253
01254
01255 uuu->net_info = xxx;
01256
01257 uuu->parse_http_hdr = parse_http_hdr;
01258 uuu->adjust_net_info = adjust_net_info;
01259 uuu->adjust_cleanup = adjust_cleanup;
01260 uuu->adjust_data = adjust_data;
01261
01262 uuu->flags = flags;
01263 uuu->reserved = 0;
01264 uuu->can_connect = eCC_Once;
01265
01266 ConnNetInfo_GetValue(0, "HTTP_ERROR_HEADER_ONLY", value, sizeof(value),"");
01267 uuu->error_header = (*value && (strcmp (value, "1") == 0 ||
01268 strcasecmp(value, "on") == 0 ||
01269 strcasecmp(value, "yes") == 0 ||
01270 strcasecmp(value, "true") == 0));
01271
01272 uuu->sock = 0;
01273 uuu->o_timeout = kDefaultTimeout;
01274 uuu->w_timeout = kDefaultTimeout;
01275 uuu->http = 0;
01276 uuu->r_buf = 0;
01277 uuu->w_buf = 0;
01278
01279
01280
01281 ccc->handle = uuu;
01282 ccc->next = 0;
01283 ccc->meta = 0;
01284 ccc->setup = s_Setup;
01285 ccc->destroy = s_Destroy;
01286
01287 return ccc;
01288 }
01289
01290
01291
01292
01293
01294
01295 extern CONNECTOR HTTP_CreateConnector
01296 (const SConnNetInfo* net_info,
01297 const char* user_header,
01298 THCC_Flags flags)
01299 {
01300 return s_CreateConnector(net_info, user_header, flags, 0, 0, 0, 0);
01301 }
01302
01303
01304 extern CONNECTOR HTTP_CreateConnectorEx
01305 (const SConnNetInfo* net_info,
01306 THCC_Flags flags,
01307 FHttpParseHTTPHeader parse_http_hdr,
01308 FHttpAdjustNetInfo adjust_net_info,
01309 void* adjust_data,
01310 FHttpAdjustCleanup adjust_cleanup)
01311 {
01312 return s_CreateConnector(net_info, 0, flags, parse_http_hdr,
01313 adjust_net_info, adjust_data, adjust_cleanup);
01314 }
01315
01316
01317 extern void HTTP_SetNcbiMessageHook(FHTTP_NcbiMessageHook hook)
01318 {
01319 if (hook) {
01320 if (hook != s_MessageHook)
01321 s_MessageIssued = s_MessageIssued ? -1 : -2;
01322 } else if (s_MessageIssued < -1)
01323 s_MessageIssued = 0;
01324 s_MessageHook = hook;
01325 }
01326
01327