static void s_Destroy(CONNECTOR connector) { SHttpConnector* uuu = (SHttpConnector*) connector->handle; if (uuu->adjust_cleanup) uuu->adjust_cleanup(uuu->adjust_data); ConnNetInfo_Destroy(uuu->net_info); BUF_Destroy(uuu->http); BUF_Destroy(uuu->r_buf); BUF_Destroy(uuu->w_buf); free(uuu); connector->handle = 0; free(connector); }
static void s_Destroy (CONNECTOR connector) { SMemoryConnector* xxx = (SMemoryConnector*) connector->handle; if (xxx->own_buf) BUF_Destroy(xxx->buf); free(xxx); connector->handle = 0; free(connector); }
/* Parse HTTP header */ static EIO_Status s_ReadHeader(SHttpConnector* uuu, char** retry, EReadMode read_mode) { ERetry redirect = eRetry_None; int server_error = 0; int http_status; EIO_Status status; char* header; size_t size; assert(uuu->sock && uuu->read_header); *retry = 0; /* line by line HTTP header input */ for (;;) { /* do we have full header yet? */ size = BUF_Size(uuu->http); if (!(header = (char*) malloc(size + 1))) { CORE_LOGF_X(7, eLOG_Error, ("[HTTP] Cannot allocate header, %lu bytes", (unsigned long) size)); return eIO_Unknown; } verify(BUF_Peek(uuu->http, header, size) == size); header[size] = '\0'; if (size >= 4 && strcmp(&header[size - 4], "\r\n\r\n") == 0) break/*full header captured*/; free(header); status = SOCK_StripToPattern(uuu->sock, "\r\n", 2, &uuu->http, 0); if (status != eIO_Success) { ELOG_Level level; if (status == eIO_Timeout) { const STimeout* tmo = SOCK_GetTimeout(uuu->sock, eIO_Read); if (!tmo) level = eLOG_Error; else if (tmo->sec | tmo->usec) level = eLOG_Warning; else if (read_mode != eRM_WaitCalled) level = eLOG_Trace; else return status; } else level = eLOG_Error; CORE_LOGF_X(8, level, ("[HTTP] Error reading header (%s)", IO_StatusStr(status))); return status; } } /* the entire header has been read */ uuu->read_header = 0/*false*/; status = eIO_Success; if (BUF_Read(uuu->http, 0, size) != size) { CORE_LOG_X(9, eLOG_Error, "[HTTP] Cannot discard HTTP header buffer"); status = eIO_Unknown; assert(0); } /* HTTP status must come on the first line of the response */ if (sscanf(header, "HTTP/%*d.%*d %d ", &http_status) != 1 || !http_status) http_status = -1; if (http_status < 200 || 299 < http_status) { server_error = http_status; if (http_status == 301 || http_status == 302) redirect = eRetry_Redirect; else if (http_status == 401) redirect = eRetry_Authenticate; else if (http_status == 407) redirect = eRetry_ProxyAuthenticate; else if (http_status < 0 || http_status == 403 || http_status == 404) uuu->net_info->max_try = 0; } uuu->code = http_status < 0 ? -1 : http_status; if ((server_error || !uuu->error_header) && uuu->net_info->debug_printout == eDebugPrintout_Some) { /* HTTP header gets printed as part of data logging when uuu->net_info->debug_printout == eDebugPrintout_Data. */ const char* header_header; if (!server_error) header_header = "HTTP header"; else if (uuu->flags & fHCC_KeepHeader) header_header = "HTTP header (error)"; else if (uuu->code == 301 || uuu->code == 302) header_header = "HTTP header (moved)"; else if (!uuu->net_info->max_try) header_header = "HTTP header (unrecoverable error)"; else header_header = "HTTP header (server error, can retry)"; CORE_DATA_X(19, header, size, header_header); } {{ /* parsing "NCBI-Message" tag */ static const char kNcbiMessageTag[] = "\n" HTTP_NCBI_MESSAGE " "; const char* s; for (s = strchr(header, '\n'); s && *s; s = strchr(s + 1, '\n')) { if (strncasecmp(s, kNcbiMessageTag, sizeof(kNcbiMessageTag)-1)==0){ const char* message = s + sizeof(kNcbiMessageTag) - 1; while (*message && isspace((unsigned char)(*message))) message++; if (!(s = strchr(message, '\r'))) s = strchr(message, '\n'); assert(s); do { if (!isspace((unsigned char) s[-1])) break; } while (--s > message); if (message != s) { if (s_MessageHook) { if (s_MessageIssued <= 0) { s_MessageIssued = 1; s_MessageHook(message); } } else { s_MessageIssued = -1; CORE_LOGF_X(10, eLOG_Critical, ("[NCBI-MESSAGE] %.*s", (int)(s - message), message)); } } break; } } }} if (uuu->flags & fHCC_KeepHeader) { if (!BUF_Write(&uuu->r_buf, header, size)) { CORE_LOG_X(11, eLOG_Error, "[HTTP] Cannot keep HTTP header"); status = eIO_Unknown; } free(header); return status; } if (uuu->parse_http_hdr && !(*uuu->parse_http_hdr)(header, uuu->adjust_data, server_error)) { server_error = 1/*fake, but still boolean true*/; } if (redirect == eRetry_Redirect) { /* parsing "Location" pointer */ static const char kLocationTag[] = "\nLocation: "; char* s; for (s = strchr(header, '\n'); s && *s; s = strchr(s + 1, '\n')) { if (strncasecmp(s, kLocationTag, sizeof(kLocationTag) - 1) == 0) { char* location = s + sizeof(kLocationTag) - 1; while (*location && isspace((unsigned char)(*location))) location++; if (!(s = strchr(location, '\r'))) s = strchr(location, '\n'); assert(s); do { if (!isspace((unsigned char) s[-1])) break; } while (--s > location); if (s != location) { size_t len = (size_t)(s - location); memmove(header, location, len); header[len] = '\0'; *retry = header; } break; } } } else if (redirect != eRetry_None) { /* parsing "Authenticate" tags */ static const char kAuthenticateTag[] = "-Authenticate: "; char* s; for (s = strchr(header, '\n'); s && *s; s = strchr(s + 1, '\n')) { if (strncasecmp(s + (redirect == eRetry_Authenticate ? 4 : 6), kAuthenticateTag, sizeof(kAuthenticateTag)-1)==0){ if ((redirect == eRetry_Authenticate && strncasecmp(s, "\nWWW", 4) != 0) || (redirect == eRetry_ProxyAuthenticate && strncasecmp(s, "\nProxy", 6) != 0)) { continue; } /* TODO */ } } } else if (!server_error) { static const char kContentLengthTag[] = "\nContent-Length: "; const char* s; for (s = strchr(header, '\n'); s && *s; s = strchr(s + 1, '\n')) { if (!strncasecmp(s,kContentLengthTag,sizeof(kContentLengthTag)-1)){ const char* expected = s + sizeof(kContentLengthTag) - 1; while (*expected && isspace((unsigned char)(*expected))) expected++; if (!(s = strchr(expected, '\r'))) s = strchr(expected, '\n'); assert(s); do { if (!isspace((unsigned char) s[-1])) break; } while (--s > expected); if (s != expected) { char* e; errno = 0; uuu->expected = (size_t) strtol(expected, &e, 10); if (errno || e != s) uuu->expected = 0; else if (!uuu->expected) uuu->expected = (size_t)(-1L); } break; } } } if (!*retry) free(header); /* skip & printout the content, if server error was flagged */ if (server_error && uuu->net_info->debug_printout == eDebugPrintout_Some) { BUF buf = 0; char* body; SOCK_SetTimeout(uuu->sock, eIO_Read, 0); /* read until EOF */ SOCK_StripToPattern(uuu->sock, 0, 0, &buf, 0); if (!(size = BUF_Size(buf))) { CORE_LOG_X(12, eLOG_Trace, "[HTTP] No HTTP body received with this error"); } else if ((body = (char*) malloc(size)) != 0) { size_t n = BUF_Read(buf, body, size); if (n != size) { CORE_LOGF_X(13, eLOG_Error, ("[HTTP] Cannot read server error " "body from buffer (%lu out of %lu)", (unsigned long) n, (unsigned long) size)); } CORE_DATA_X(20, body, n, "Server error body"); free(body); } else { CORE_LOGF_X(14, eLOG_Error, ("[HTTP] Cannot allocate server error " "body, %lu bytes", (unsigned long) size)); } BUF_Destroy(buf); } return server_error ? eIO_Unknown : status; }
char* SERV_Print(SERV_ITER iter, SConnNetInfo* net_info, int/*bool*/ but_last) { static const char kClientRevision[] = "Client-Revision: %hu.%hu\r\n"; static const char kAcceptedServerTypes[] = "Accepted-Server-Types:"; static const char kUsedServerInfo[] = "Used-Server-Info: "; static const char kNcbiFWPorts[] = "NCBI-Firewall-Ports: "; static const char kServerCount[] = "Server-Count: "; static const char kPreference[] = "Preference: "; static const char kSkipInfo[] = "Skip-Info-%u: "; static const char kAffinity[] = "Affinity: "; char buffer[128], *str; size_t buflen, i; TSERV_Type t; BUF buf = 0; /* Put client version number */ buflen = sprintf(buffer, kClientRevision, SERV_CLIENT_REVISION_MAJOR, SERV_CLIENT_REVISION_MINOR); assert(buflen < sizeof(buffer)); if (!BUF_Write(&buf, buffer, buflen)) { BUF_Destroy(buf); return 0; } if (iter) { if (net_info && !net_info->http_referer && iter->op && iter->op->name) s_SetDefaultReferer(iter, net_info); /* Accepted server types */ buflen = sizeof(kAcceptedServerTypes) - 1; memcpy(buffer, kAcceptedServerTypes, buflen); for (t = 1; t; t <<= 1) { if (iter->type & t) { const char* name = SERV_TypeStr((ESERV_Type) t); size_t namelen = strlen(name); if (!namelen || buflen + 1 + namelen + 2 >= sizeof(buffer)) break; buffer[buflen++] = ' '; memcpy(buffer + buflen, name, namelen); buflen += namelen; } } if (buffer[buflen - 1] != ':') { strcpy(&buffer[buflen], "\r\n"); assert(strlen(buffer) == buflen+2 && buflen+2 < sizeof(buffer)); if (!BUF_Write(&buf, buffer, buflen + 2)) { BUF_Destroy(buf); return 0; } } if (iter->ismask || (iter->pref && (iter->host | iter->port))) { /* FIXME: To obsolete? */ /* How many server-infos for the dispatcher to send to us */ if (!BUF_Write(&buf, kServerCount, sizeof(kServerCount) - 1) || !BUF_Write(&buf, iter->ismask ? "10\r\n" : "ALL\r\n", iter->ismask ? 4 : 5)) { BUF_Destroy(buf); return 0; } } if (iter->type & fSERV_Firewall) { /* Firewall */ s_PrintFirewallPorts(buffer, sizeof(buffer), net_info); if (*buffer && (!BUF_Write(&buf, kNcbiFWPorts, sizeof(kNcbiFWPorts)-1) || !BUF_Write(&buf, buffer, strlen(buffer)) || !BUF_Write(&buf, "\r\n", 2))) { BUF_Destroy(buf); return 0; } } if (iter->pref && (iter->host | iter->port)) { /* Preference */ buflen = SOCK_HostPortToString(iter->host, iter->port, buffer, sizeof(buffer)); buflen += sprintf(buffer + buflen, " %.2lf%%\r\n", iter->pref*1e2); if (!BUF_Write(&buf, kPreference, sizeof(kPreference) - 1) || !BUF_Write(&buf, buffer, buflen)) { BUF_Destroy(buf); return 0; } } if (iter->arglen) { /* Affinity */ if (!BUF_Write(&buf, kAffinity, sizeof(kAffinity) - 1) || !BUF_Write(&buf, iter->arg, iter->arglen) || (iter->val && (!BUF_Write(&buf, "=", 1) || !BUF_Write(&buf, iter->val, iter->vallen))) || !BUF_Write(&buf, "\r\n", 2)) { BUF_Destroy(buf); return 0; } } /* Drop any outdated skip entries */ iter->time = (TNCBI_Time) time(0); s_SkipSkip(iter); /* Put all the rest into rejection list */ for (i = 0; i < iter->n_skip; i++) { /* NB: all skip infos are now kept with names (perhaps, empty) */ const char* name = SERV_NameOfInfo(iter->skip[i]); size_t namelen = name && *name ? strlen(name) : 0; if (!(str = SERV_WriteInfo(iter->skip[i]))) break; if (but_last && iter->last == iter->skip[i]) { buflen = sizeof(kUsedServerInfo) - 1; memcpy(buffer, kUsedServerInfo, buflen); } else buflen = sprintf(buffer, kSkipInfo, (unsigned) i + 1); assert(buflen < sizeof(buffer) - 1); if (!BUF_Write(&buf, buffer, buflen) || (namelen && !BUF_Write(&buf, name, namelen)) || (namelen && !BUF_Write(&buf, " ", 1)) || !BUF_Write(&buf, str, strlen(str)) || !BUF_Write(&buf, "\r\n", 2)) { free(str); break; } free(str); } if (i < iter->n_skip) { BUF_Destroy(buf); return 0; } } /* Ok then, we have filled the entire header, <CR><LF> terminated */ if ((buflen = BUF_Size(buf)) != 0) { if ((str = (char*) malloc(buflen + 1)) != 0) { if (BUF_Read(buf, str, buflen) != buflen) { free(str); str = 0; } else str[buflen] = '\0'; } } else str = 0; BUF_Destroy(buf); return str; }
extern int main(void) { # define X_MAX_N_IO (unsigned) 4 # define X_MAX_READ (size_t) (3 * BUF_DEF_CHUNK_SIZE) # define X_TIMES (unsigned) (s_Rand() % X_MAX_N_IO) # define X_BYTES (size_t) (s_Rand() % X_MAX_READ) BUF buf = 0; BUF buf1 = 0; int/*bool*/ do_loop = 1 /* true */; /* a simple test */ {{ char charbuf[128]; assert(BUF_PushBack(&buf, (const char*) "0", 1)); assert(BUF_Write(&buf, (const char*) "1", 1)); assert(BUF_Peek(buf, charbuf, sizeof(charbuf)) == 2); assert(BUF_PushBack(&buf, (const char*) "BB", 2)); assert(BUF_PushBack(&buf, (const char*) "aa", 2)); assert(BUF_Write(&buf, (const char*) "23\0", 3)); assert(BUF_Read(buf, charbuf, sizeof(charbuf)) == 9); assert(strcmp(charbuf, (const char*) "aaBB0123") == 0); assert(BUF_Prepend(&buf, "Hello World\0", 12)); assert(BUF_Read(buf, 0, 6) == 6); assert(BUF_PushBack(&buf, "Goodbye ", 8)); assert(BUF_Read(buf, charbuf, sizeof(charbuf)) == 14); assert(strcmp(charbuf, (const char*) "Goodbye World") == 0); BUF_Destroy(buf); buf = 0; }} /* usage */ fprintf(stderr, "Waiting for the data in STDIN...\n"); /* read up to the very end of input stream */ while ( do_loop ) { char charbuf[X_MAX_READ]; unsigned i, n_times; /* read from the input stream, write to the NCBI IO-buf */ n_times = X_TIMES; for (i = 0; i < n_times; i++) { size_t n_bytes = X_BYTES; if ( !n_bytes ) continue; n_bytes = fread(charbuf, 1, n_bytes, stdin); fprintf(stderr, "STDIN %5lu\n", (unsigned long) n_bytes); if ( !n_bytes ) { do_loop = 0 /* false */; /* end of the input stream */ break; } assert(BUF_Write(&buf, charbuf, n_bytes)); assert(BUF_Write(&buf1, charbuf, n_bytes)); fprintf(stderr, "BUF_Write %5lu\n", (unsigned long) n_bytes); } /* peek & read from the NCBI IO-buf, write to the output stream */ n_times = X_TIMES; for (i = 0; i < n_times && BUF_Size(buf); i++) { int/*bool*/ do_peek = (s_Rand() % 2 == 0); size_t n_peek = 0; size_t n_bytes = X_BYTES; if ( !n_bytes ) continue; /* peek from the NCBI IO-buf */ if ( do_peek ) { unsigned j, n_peek_times = s_Rand() % 3 + 1; for (j = 0; j < n_peek_times; j++) { n_peek = BUF_Peek(buf, charbuf, n_bytes); fprintf(stderr, "\tBUF_Peek %5lu\n",(unsigned long)n_peek); } } /* read (or just discard) the data */ if (do_peek && s_Rand() % 2 == 0) n_bytes = BUF_Read(buf, 0, n_bytes); else n_bytes = BUF_Read(buf, charbuf, n_bytes); fprintf(stderr, "\t\tBUF_Read %5lu\n", (unsigned long) n_bytes); assert(!do_peek || n_bytes == n_peek); /* push back & re-read */ if (s_Rand() % 3 == 0) { size_t n_pushback = s_Rand() % n_bytes; if (n_pushback == 0) n_pushback = 1; assert(BUF_PushBack (&buf, charbuf + n_bytes - n_pushback, n_pushback)); assert(BUF_Read ( buf, charbuf + n_bytes - n_pushback, n_pushback)); } /* write the read data to the output stream */ assert(n_bytes == fwrite(charbuf, 1, n_bytes, stdout)); fprintf(stderr, "\t\tSTDOUT %5lu\n", (unsigned long) n_bytes); } } /* flush the IO-buf to the output stream */ while ( BUF_Size(buf) ) { char charbuf[256]; size_t n_bytes = BUF_Read(buf, charbuf, sizeof(charbuf)); {{ char tmp[sizeof(charbuf)]; size_t n_pushback = s_Rand() % 64; if (n_pushback > n_bytes) n_pushback = n_bytes; assert(BUF_PushBack (&buf, charbuf + n_bytes - n_pushback, n_pushback)); assert(BUF_Read ( buf, tmp, n_pushback) == n_pushback); memcpy(charbuf + n_bytes - n_pushback, tmp, n_pushback); }} fprintf(stderr, "\t\tBUF_Read/flush %5lu\n", (unsigned long) n_bytes); assert(n_bytes); assert(n_bytes == fwrite(charbuf, 1, n_bytes, stdout)); fprintf(stderr, "\t\tSTDOUT /flush %5lu\n", (unsigned long) n_bytes); } fflush(stdout); /* Test for "BUF_PeekAt()" */ {{ size_t buf1_size = BUF_Size(buf1); int n; assert(buf1_size > 0); for (n = 0; n < 20; n++) { size_t pos; size_t size; /* Erratically copy "buf1" to "buf" */ for (pos = 0; pos < buf1_size; pos += size) { char temp_buf[BUF_DEF_CHUNK_SIZE * 2]; size_t n_peeked; size = s_Rand() % (BUF_DEF_CHUNK_SIZE * 2); n_peeked = BUF_PeekAt(buf1, pos, temp_buf, size); if (pos + size <= buf1_size) { assert(n_peeked == size); } else { assert(n_peeked == buf1_size - pos); } assert(BUF_PeekAt(buf1, pos, temp_buf, size) == n_peeked); assert(BUF_Write(&buf, temp_buf, n_peeked)); } /* Compare "buf" and "buf1"; empty up "buf" in process */ assert(BUF_Size(buf1) == BUF_Size(buf)); for (pos = 0; pos < buf1_size; pos += size) { char bb[1024]; char b1[1024]; assert(sizeof(bb) == sizeof(b1)); size = BUF_Read(buf, bb, sizeof(bb)); assert(BUF_PeekAt(buf1, pos, b1, size) == size); assert(size <= sizeof(b1)); assert(memcmp(bb, b1, size) == 0); } assert(pos == buf1_size); assert(BUF_Size(buf1) == buf1_size); assert(BUF_Size(buf) == 0); } }} /* cleanup & exit */ BUF_Destroy(buf1); BUF_Destroy(buf); return 0; }