/* Send the accumulated output data(if any) to server, then close socket. * Regardless of the flush, clear both input and output buffer. * This function is only called to either re-open or close the connector. */ static void s_FlushAndDisconnect(SHttpConnector* uuu, int/*bool*/ close, const STimeout* timeout) { size_t w_size; /* store timeouts for later use */ if (timeout) { uuu->oo_timeout = *timeout; uuu->o_timeout = &uuu->oo_timeout; uuu->ww_timeout = *timeout; uuu->w_timeout = &uuu->ww_timeout; } else { uuu->o_timeout = timeout; uuu->w_timeout = timeout; } if (close && uuu->can_connect != eCC_None && !uuu->sock && ((uuu->flags & fHCC_SureFlush) || BUF_Size(uuu->w_buf))) { /* "WRITE" mode and data (or just flag) pending */ s_PreRead(uuu, timeout, eRM_DropUnread); } s_Disconnect(uuu, timeout, eRM_DropUnread); assert(!uuu->sock); /* clear pending output data, if any */ w_size = BUF_Size(uuu->w_buf); if (w_size && BUF_Read(uuu->w_buf, 0, w_size) != w_size) { CORE_LOG_X(18, eLOG_Error, "[HTTP] Cannot drop output buffer"); assert(0); } }
static EIO_Status s_VT_Wait (CONNECTOR connector, EIO_Event event, const STimeout* timeout) { SHttpConnector* uuu = (SHttpConnector*) connector->handle; switch (event) { case eIO_Read: if (uuu->can_connect == eCC_None) return eIO_Closed; if (!uuu->sock || uuu->read_header) { EIO_Status status = s_PreRead(uuu, timeout, eRM_WaitCalled); if (status != eIO_Success || BUF_Size(uuu->r_buf)) return status; assert(uuu->sock); } return SOCK_Wait(uuu->sock, eIO_Read, timeout); case eIO_Write: /* Return 'Closed' if no more writes are allowed (and now - reading) */ return uuu->can_connect == eCC_None || (uuu->sock && uuu->can_connect == eCC_Once) ? eIO_Closed : eIO_Success; default: assert(0); return eIO_InvalidArg; } }
/* Reset/readout input data and close socket */ static EIO_Status s_Disconnect(SHttpConnector* uuu, const STimeout* timeout, EReadMode read_mode) { EIO_Status status = eIO_Success; if (read_mode == eRM_DropUnread) { size_t r_size = BUF_Size(uuu->r_buf); if (r_size && BUF_Read(uuu->r_buf, 0, r_size) != r_size) { CORE_LOG_X(17, eLOG_Error, "[HTTP] Cannot drop input buffer"); assert(0); } } else if ((status = s_PreRead(uuu, timeout, read_mode)) == eIO_Success) { do { char buf[4096]; size_t x_read; status = s_Read(uuu, buf, sizeof(buf), &x_read); if (!BUF_Write(&uuu->r_buf, buf, x_read)) status = eIO_Unknown; } while (status == eIO_Success); if (status == eIO_Closed) status = eIO_Success; } if (uuu->sock) /* s_PreRead() might have dropped the connection already */ s_DropConnection(uuu, timeout); if (uuu->can_connect == eCC_Once) uuu->can_connect = eCC_None; return status; }
/// @internal static string s_ReadStrFromBUF(BUF buf) { size_t size = BUF_Size(buf); string ret(size, '\0'); BUF_Read(buf, &ret[0], size); return ret; }
/* Unconditionally drop the connection; timeout may specify time allowance */ static void s_DropConnection(SHttpConnector* uuu, const STimeout* timeout) { size_t http_size = BUF_Size(uuu->http); assert(uuu->sock); if (http_size && BUF_Read(uuu->http, 0, http_size) != http_size) { CORE_LOG_X(4, eLOG_Error, "[HTTP] Cannot discard HTTP header buffer"); assert(0); } SOCK_SetTimeout(uuu->sock, eIO_Close, timeout); SOCK_Close(uuu->sock); uuu->sock = 0; }
/* Prepare connector for reading. Open socket if necessary and * make initial connect and send, re-trying if possible until success. * Return codes: * eIO_Success = success, connector is ready for reading (uuu->sock != NULL); * eIO_Timeout = maybe (check uuu->sock) connected and no data yet available; * other code = error, not connected (uuu->sock == NULL). */ static EIO_Status s_PreRead(SHttpConnector* uuu, const STimeout* timeout, EReadMode read_mode) { EIO_Status status; char* retry; for (;;) { status = s_ConnectAndSend(uuu, read_mode); if (!uuu->sock) { assert(status != eIO_Success); break; } if (status != eIO_Success) { if (status != eIO_Timeout || status == SOCK_Status(uuu->sock, eIO_Read)/*pending*/) break; } /* set timeout */ SOCK_SetTimeout(uuu->sock, eIO_Read, timeout); if (!uuu->read_header) break; if ((status = s_ReadHeader(uuu, &retry, read_mode)) == eIO_Success) { size_t w_size = BUF_Size(uuu->w_buf); assert(!uuu->read_header && !retry); /* pending output data no longer needed */ if (BUF_Read(uuu->w_buf, 0, w_size) != w_size) { CORE_LOG_X(15, eLOG_Error, "[HTTP] Cannot discard output buffer"); assert(0); } break; } /* if polling then bail out with eIO_Timeout */ if(status == eIO_Timeout && timeout && !(timeout->sec | timeout->usec)) break; /* HTTP header read error; disconnect and try to use another server */ SOCK_Abort(uuu->sock); s_DropConnection(uuu, 0/*no wait*/); if (!s_Adjust(uuu, &retry, read_mode)) { uuu->can_connect = eCC_None; break; } } return status; }
CConn_MemoryStream::CConn_MemoryStream(BUF buf, EOwnership owner, size_t buf_size) : CConn_IOStream(TConn_Pair(MEMORY_CreateConnectorEx(buf, owner == eTakeOwnership ? 1/*true*/ : 0/*false*/), eIO_Unknown), 0, buf_size, fConn_ReadBuffered | fConn_WriteBuffered, 0, BUF_Size(buf)), m_Ptr(0) { return; }
/*ARGSUSED*/ static EIO_Status s_VT_Wait (CONNECTOR connector, EIO_Event event, const STimeout* timeout) { SMemoryConnector* xxx = (SMemoryConnector*) connector->handle; assert(event == eIO_Read || event == eIO_Write); switch (event) { case eIO_Read: if (!BUF_Size(xxx->buf)) return eIO_Closed; /*FALLTHRU*/ case eIO_Write: return eIO_Success; default: assert(0); break; } return eIO_InvalidArg; }
/* 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; }
/* Connect to the server specified by uuu->net_info, then compose and form * relevant HTTP header, and flush the accumulated output data(uuu->w_buf) * after the HTTP header. If connection/write unsuccessful, retry to reconnect * and send the data again until permitted by s_Adjust(). */ static EIO_Status s_ConnectAndSend(SHttpConnector* uuu, EReadMode read_mode) { EIO_Status status; for (;;) { char* null = 0; if (!uuu->sock) { if ((status = s_Connect(uuu, read_mode)) != eIO_Success) break; assert(uuu->sock); uuu->read_header = 1/*true*/; uuu->shut_down = 0/*false*/; uuu->expected = 0; uuu->received = 0; uuu->code = 0; } else status = eIO_Success; if (uuu->w_len) { size_t off = BUF_Size(uuu->w_buf) - uuu->w_len; SOCK_SetTimeout(uuu->sock, eIO_Write, uuu->w_timeout); do { char buf[4096]; size_t n_written; size_t n_write = BUF_PeekAt(uuu->w_buf, off, buf, sizeof(buf)); status = SOCK_Write(uuu->sock, buf, n_write, &n_written, eIO_WritePlain); if (status != eIO_Success) break; uuu->w_len -= n_written; off += n_written; } while (uuu->w_len); } else if (!uuu->shut_down) status = SOCK_Write(uuu->sock, 0, 0, 0, eIO_WritePlain); if (status == eIO_Success) { assert(uuu->w_len == 0); if (!uuu->shut_down) { /* 10/07/03: While this call here is perfectly legal, it could * cause connection severed by a buggy CISCO load-balancer. */ /* 10/28/03: CISCO's beta patch for their LB shows that the * problem has been fixed; no more 2'30" drops in connections * that shut down for write. We still leave this commented * out to allow unpatched clients to work seamlessly... */ /*SOCK_Shutdown(uuu->sock, eIO_Write);*/ uuu->shut_down = 1; } break; } if (status == eIO_Timeout && uuu->w_timeout && !(uuu->w_timeout->sec | uuu->w_timeout->usec)) { break; } CORE_LOGF_X(6, eLOG_Error, ("[HTTP] Error writing body at offset %lu (%s)", (unsigned long)(BUF_Size(uuu->w_buf) - uuu->w_len), IO_StatusStr(status))); /* write failed; close and try to use another server */ SOCK_Abort(uuu->sock); s_DropConnection(uuu, 0/*no wait*/); if (!s_Adjust(uuu, &null, read_mode)) { uuu->can_connect = eCC_None; status = eIO_Closed; break; } } return status; }
/* Connect to the HTTP server, specified by uuu->net_info's "port:host". * Return eIO_Success only if socket connection has succeeded and uuu->sock * is non-zero. If unsuccessful, try to adjust uuu->net_info by s_Adjust(), * and then re-try the connection attempt. */ static EIO_Status s_Connect(SHttpConnector* uuu, EReadMode read_mode) { EIO_Status status; assert(!uuu->sock); if (uuu->can_connect == eCC_None) { CORE_LOG_X(5, eLOG_Error, "[HTTP] Connector no longer usable"); return eIO_Closed; } /* the re-try loop... */ for (;;) { int/*bool*/ reset_user_header = 0; char* http_user_header = 0; char* null = 0; TSOCK_Flags flags; uuu->w_len = BUF_Size(uuu->w_buf); if (uuu->net_info->http_user_header) http_user_header = strdup(uuu->net_info->http_user_header); if (!uuu->net_info->http_user_header == !http_user_header) { ConnNetInfo_ExtendUserHeader (uuu->net_info, "User-Agent: NCBIHttpConnector" #ifdef NCBI_CXX_TOOLKIT " (C++ Toolkit)" #else " (C Toolkit)" #endif "\r\n"); reset_user_header = 1; } if (uuu->net_info->debug_printout) ConnNetInfo_Log(uuu->net_info, CORE_GetLOG()); flags = (uuu->net_info->debug_printout == eDebugPrintout_Data ? fSOCK_LogOn : fSOCK_LogDefault); if (uuu->net_info->scheme == eURL_Https) flags |= fSOCK_Secure; if (!(uuu->flags & fHCC_NoUpread)) flags |= fSOCK_ReadOnWrite; /* connect & send HTTP header */ if ((status = URL_ConnectEx (uuu->net_info->host, uuu->net_info->port, uuu->net_info->path, uuu->net_info->args, uuu->net_info->req_method, uuu->w_len, uuu->o_timeout, uuu->w_timeout, uuu->net_info->http_user_header, (int/*bool*/)(uuu->flags & fHCC_UrlEncodeArgs), flags, &uuu->sock)) != eIO_Success) { uuu->sock = 0; } if (reset_user_header) { ConnNetInfo_SetUserHeader(uuu->net_info, 0); uuu->net_info->http_user_header = http_user_header; } if (uuu->sock) break/*success*/; /* connection failed, no socket was created */ if (!s_Adjust(uuu, &null, read_mode)) break/*closed*/; } return 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; }