NCBI C++ ToolKit
ncbi_http_connector.c
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: ncbi_http_connector.c 101457 2023-12-15 16:56:06Z lavr $
2  * ===========================================================================
3  *
4  * PUBLIC DOMAIN NOTICE
5  * National Center for Biotechnology Information
6  *
7  * This software/database is a "United States Government Work" under the
8  * terms of the United States Copyright Act. It was written as part of
9  * the author's official duties as a United States Government employee and
10  * thus cannot be copyrighted. This software/database is freely available
11  * to the public for use. The National Library of Medicine and the U.S.
12  * Government have not placed any restriction on its use or reproduction.
13  *
14  * Although all reasonable efforts have been taken to ensure the accuracy
15  * and reliability of the software and data, the NLM and the U.S.
16  * Government do not and cannot warrant the performance or results that
17  * may be obtained by using this software or data. The NLM and the U.S.
18  * Government disclaim all warranties, express or implied, including
19  * warranties of performance, merchantability or fitness for any particular
20  * purpose.
21  *
22  * Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author: Anton Lavrentiev, Denis Vakatov
27  *
28  * File Description:
29  * Implement CONNECTOR for the HTTP-based network connection
30  *
31  * See in "ncbi_connector.h" for the detailed specification of the underlying
32  * connector("CONNECTOR", "SConnectorTag") methods and structures.
33  *
34  */
35 
36 #include "ncbi_ansi_ext.h"
37 #include "ncbi_comm.h"
38 #include "ncbi_priv.h"
39 #include "ncbi_servicep.h"
40 #include <connect/ncbi_base64.h>
42 #include <ctype.h>
43 #include <errno.h>
44 #include <stdlib.h>
45 
46 #define NCBI_USE_ERRCODE_X Connect_HTTP
47 
48 
49 #define HTTP_SOAK_READ_SIZE 16384
50 
51 
52 /***********************************************************************
53  * INTERNAL -- Auxiliary types and static functions
54  ***********************************************************************/
55 
56 
57 /* Connection states, see table below
58  */
59 enum EConnState {
65  eCS_DoneBody = 5, /* NB: |eCS_ReadBody */
66  eCS_Discard = 7, /* NB: |eCS_DoneBody */
67  eCS_Eom = 0xF /* NB: |eCS_Discard */
68 };
69 typedef unsigned EBConnState; /* packed EConnState */
70 
71 
72 /* Whether the connector is allowed to connect
73  * NB: In order to be able to connect, fCC_Once must be set.
74  */
76  fCC_None, /* 0 */
77  fCC_Once, /* 1 */
78  fCC_Unlimited /* 0|2 */
79 };
80 typedef unsigned EBCanConnect; /* packed ECanConnect */
81 
82 typedef unsigned EBSwitch; /* packed ESwitch */
83 
84 typedef enum {
85  eEM_Drop, /* 0 */
86  eEM_Wait, /* 1 */
87  eEM_Read, /* 2 */
88  eEM_Flush /* 1|2 */
90 
91 
92 typedef enum {
93  eRetry_None = 0, /* 0 */
94  eRetry_Unused = 1, /* yet */
95  eRetry_Redirect = 2, /* 2 */
96  eRetry_Redirect303 = 3, /* 2|1 */
97  eRetry_Authenticate = 4, /* 4 */
98  eRetry_ProxyAuthenticate = 5 /* 4|1 */
100 
101 typedef struct {
103  const char* data;
104 } SRetry;
105 
106 
107 /* All internal data necessary to perform the (re)connect and I/O.
108  *
109  * The following connection states are defined: | sock?
110  * --------------+--------------------------------------------------+---------
111  * NotInitiated | HTTP request needs to be (re-)initiated |
112  * WriteRequest | HTTP request body is being written | != NULL
113  * FlushRequest | HTTP request is being completed and flushed out | != NULL
114  * ReadHeader | HTTP response header is being read | != NULL
115  * ReadBody | HTTP response body is being read | != NULL
116  * DoneBody | HTTP body is all read out of the connection | != NULL
117  * Discard | HTTP data (if any) to be discarded | != NULL
118  * Eom | HTTP message has been completed (end of message) |
119  * --------------+--------------------------------------------------+---------
120  */
121 typedef struct {
122  SConnNetInfo* net_info; /* network configuration parameters */
123  FHTTP_ParseHeader parse_header; /* callback to parse HTTP reply header */
124  void* user_data; /* user data handle for callbacks (CB) */
125  FHTTP_Adjust adjust; /* on-the-fly net_info adjustment CB */
126  FHTTP_Cleanup cleanup; /* cleanup callback */
127 
128  THTTP_Flags flags; /* as passed to constructor */
129  EBSwitch unsafe_redir:2; /* if unsafe redirects are allowed */
130  EBSwitch error_header:2; /* only err.HTTP header on SOME debug */
131  EBCanConnect can_connect:2; /* whether more connections permitted */
132  EBConnState conn_state:4; /* connection state per table above */
133  unsigned auth_done:1; /* website authorization sent */
134  unsigned proxy_auth_done:1; /* proxy authorization sent */
135  unsigned keepalive:1; /* keep-alive connection */
136  unsigned chunked:1; /* if writing/reading chunked, HTTP/1.1*/
137  unsigned entity:1; /* if there's entity payload (body)/1.1*/
138  unsigned reused:1; /* if connection was re-used */
139  unsigned retry:1; /* if the request is being re-tried */
140  unsigned reserved:7;
141  unsigned char minor_fault; /* incr each minor failure since major */
142  unsigned short major_fault; /* incr each major failure since open */
143  unsigned short http_code; /* last HTTP response code */
144 
145  const char* vhost; /* VHost in the request */
146 
147  SOCK sock; /* socket; NULL if not connected */
148  const STimeout* o_timeout; /* NULL(infinite), dflt or ptr to next */
149  STimeout oo_timeout; /* storage for (finite) open timeout */
150  const STimeout* w_timeout; /* NULL(infinite), dflt or ptr to next */
151  STimeout ww_timeout; /* storage for a (finite) write tmo */
152 
153  BUF http; /* storage for HTTP reply */
154  BUF r_buf; /* storage to accumulate input data */
155  BUF w_buf; /* storage to accumulate output data */
156  size_t w_len; /* pending message body size */
157 
158  TNCBI_BigCount expected; /* expected to receive before end/EOF */
159  TNCBI_BigCount received; /* actually received so far */
161 
162 
163 static const char kHttpHostTag[] = "Host: ";
164 static const STimeout kZeroTimeout = {0, 0};
165 
166 
167 /* NCBI messaging support */
168 static int s_MessageIssued = 0;
170 
171 
172 static int/*bool*/ x_UnsafeRedirectOK(SHttpConnector* uuu)
173 {
174  if (uuu->unsafe_redir == eDefault) {
175  if (!(uuu->flags & fHTTP_UnsafeRedirects)) {
176  char val[32];
178  "HTTP_UNSAFE_REDIRECTS",
179  val, sizeof(val), 0);
181  } else
182  uuu->unsafe_redir = eOn;
183  }
184  return uuu->unsafe_redir == eOn ? 1/*true*/ : 0/*false*/;
185 }
186 
187 
188 typedef enum {
189  /* Negative code: NOP; positive: Error */
190  eHTTP_AuthMissing = -3, /* No auth info available */
191  eHTTP_AuthUnsafe = -2, /* Auth would be unsafe */
192  eHTTP_AuthDone = -1, /* Auth already sent */
193  eHTTP_AuthOK = 0, /* Success */
194  eHTTP_AuthError = 1, /* Auth can't/doesn't work */
195  eHTTP_AuthIllegal = 3, /* Auth won't work now */
196  eHTTP_AuthNotSupp = 2, /* Unknown auth parameters */
197  eHTTP_AuthConnect = 4 /* Auth with CONNECT */
199 
200 
202  ERetry auth,
203  int/*bool*/ retry)
204 {
205  static const char kAuthTagTemplate[] = "Proxy-Authorization: Basic ";
206  char buf[80 + (CONN_USER_LEN + CONN_PASS_LEN)*3], *s;
207  size_t taglen, userlen, passlen, len, n;
208  const char *tag, *user, *pass;
209 
210  switch (auth) {
211  case eRetry_Authenticate:
212  if (uuu->auth_done)
213  return eHTTP_AuthDone;
214  tag = kAuthTagTemplate + 6;
215  taglen = sizeof(kAuthTagTemplate) - 7;
216  user = uuu->net_info->user;
217  pass = uuu->net_info->pass;
218  break;
220  if (uuu->proxy_auth_done)
221  return eHTTP_AuthDone;
222  if (!uuu->net_info->http_proxy_host[0] ||
223  !uuu->net_info->http_proxy_port) {
224  return retry ? eHTTP_AuthError : eHTTP_AuthMissing;
225  }
226  tag = kAuthTagTemplate;
227  taglen = sizeof(kAuthTagTemplate) - 1;
228  user = uuu->net_info->http_proxy_user;
229  pass = uuu->net_info->http_proxy_pass;
230  break;
231  default:
232  assert(0);
233  return eHTTP_AuthError;
234  }
235  assert(tag && user && pass);
236  if (!*user)
237  return eHTTP_AuthMissing;
238  if (retry && uuu->entity)
239  return eHTTP_AuthIllegal;
240  if (auth == eRetry_Authenticate) {
241  if (uuu->net_info->scheme != eURL_Https
242  && !x_UnsafeRedirectOK(uuu)) {
243  return eHTTP_AuthUnsafe;
244  }
245  uuu->auth_done = 1/*true*/;
246  } else
247  uuu->proxy_auth_done = 1/*true*/;
248  userlen = strlen(user);
249  passlen = strlen(pass);
250  s = buf + sizeof(buf) - passlen;
251  if (passlen > 2 && pass[0] == '[' && pass[passlen - 1] == ']') {
252  if (BASE64_Decode(pass + 1, passlen - 2, &len, s, passlen, &n)
253  && len == passlen - 2) {
254  len = n;
255  } else
256  len = 0;
257  } else
258  len = 0;
259  s -= userlen;
260  memcpy(--s, user, userlen);
261  s[userlen++] = ':';
262  if (!len) {
263  memcpy(s + userlen, pass, passlen);
264  len = userlen + passlen;
265  } else
266  len += userlen;
267  /* usable room */
268  userlen = (size_t)(s - buf);
269  assert(userlen > taglen);
270  userlen -= taglen + 1;
271  passlen = 0;
272  BASE64_Encode(s, len, &n, buf + taglen, userlen, &passlen, &passlen/*0*/);
273  if (len != n || buf[taglen + passlen])
274  return eHTTP_AuthError;
275  memcpy(buf, tag, taglen);
277 }
278 
279 
280 #ifdef __GNUC__
281 inline
282 #endif /*__GNUC__*/
283 static int/*bool*/ x_SameScheme(EBURLScheme scheme1, EBURLScheme scheme2)
284 {
285  if (!scheme1)
286  scheme1 = eURL_Http;
287  if (!scheme2)
288  scheme2 = eURL_Http;
289  return scheme1 == scheme2 ? 1/*true*/ : 0/*false*/;
290 }
291 
292 
293 static int/*bool*/ x_SameProxyHost(const char* host1, const char* host2)
294 {
295  char buf1[CONN_HOST_LEN+1], buf2[CONN_HOST_LEN+1];
296  unsigned int ip1, ip2;
297 
298  if (strcasecmp(host1, host2) == 0)
299  return 1/*true*/;
300  if (!(ip1 = SOCK_gethostbyname(host1)) || ip1 == (unsigned int)(-1))
301  return 0/*false*/;
302  if (!(ip2 = SOCK_gethostbyname(host2)) || ip2 == (unsigned int)(-1))
303  return 0/*false*/;
304  if (ip1 == ip2)
305  return 1/*true*/;
306  SOCK_gethostbyaddr(ip1, buf1, sizeof(buf1));
307  SOCK_gethostbyaddr(ip2, buf2, sizeof(buf2));
308  return *buf1 && strcasecmp(buf1, buf2) == 0 ? 1/*true*/ : 0/*false*/;
309 }
310 
311 
312 static unsigned short x_PortForScheme(unsigned port, EBURLScheme scheme)
313 {
314  if (!port) {
315  switch (scheme) {
316  case eURL_Http:
317  return CONN_PORT_HTTP;
318  case eURL_Https:
319  return CONN_PORT_HTTPS;
320  default:
321  assert(!scheme);
322  break;
323  }
324  }
325  return port;
326 }
327 
328 
329 #ifdef __GNUC__
330 inline
331 #endif /*__GNUC__*/
332 static int/*bool*/ x_SamePort(unsigned short port1, EBURLScheme scheme1,
333  unsigned short port2, EBURLScheme scheme2)
334 {
335  return x_PortForScheme(port1, scheme1) == x_PortForScheme(port2, scheme2)
336  ? 1/*true*/ : 0/*false*/;
337 }
338 
339 
340 static int/*bool*/ s_CallAdjust(SHttpConnector* uuu, unsigned int arg)
341 {
342  int retval;
344  if (!net_info)
345  return 0/*failure*/;
346  retval = uuu->adjust(uuu->net_info, uuu->user_data, arg);
347  if (retval/*advisory of no change if < 0 but we don't trust it :-)*/) {
348  int same_host = -1/*undef*/;
349  int same_port = -1/*undef*/;
350  if (uuu->sock) {
351  int/*bool*/ close = 0/*false*/;
353  net_info->http_proxy_host)
354  || (uuu->net_info->http_proxy_host[0] &&
355  uuu->net_info->http_proxy_port !=
356  net_info->http_proxy_port)){
357  close = 1/*true*/;
358  } else if (net_info->http_proxy_host[0] &&
359  net_info->http_proxy_port) {
360  if (net_info->scheme == eURL_Https) {
361  if (uuu->net_info->scheme != eURL_Https)
362  close = 1/*true*/;
363  else if (!(same_port = x_SamePort(uuu->net_info->port,
364  uuu->net_info->scheme,
365  net_info->port,
366  net_info->scheme))||
367  !(same_host = !strcasecmp(uuu->net_info->host,
368  net_info->host))) {
369  close = 1/*true*/;
370  }
371  }
372  /* connection reused with HTTP and w/CONNECT: HTTP -> HTTPS */
373  } else if (!x_SameScheme(uuu->net_info->scheme,
374  net_info->scheme) ||
375  !(same_port = x_SamePort(uuu->net_info->port,
376  uuu->net_info->scheme,
377  net_info->port,
378  net_info->scheme)) ||
379  !(same_host = !strcasecmp(uuu->net_info->host,
380  net_info->host))) {
381  close = 1/*true*/;
382  }
383  if (close) {
384  SOCK_Destroy(uuu->sock);
385  uuu->sock = 0;
386  }
387  }
388  if (uuu->vhost
389  && (!same_port || !same_host ||
390  (same_port < 0 && !x_SamePort(uuu->net_info->port,
391  uuu->net_info->scheme,
392  net_info->port,
393  net_info->scheme)) ||
394  (same_host < 0 && strcasecmp(uuu->net_info->host,
395  net_info->host) != 0))) {
396  free((void*) uuu->vhost);
397  uuu->vhost = 0;
398  }
399  }
400  ConnNetInfo_Destroy(net_info);
401  return retval;
402 }
403 
404 
405 
406 #ifdef __GNUC__
407 inline
408 #endif /*__GNUC__*/
409 static int/*bool*/ x_SameOrDefPort(unsigned short port1, EBURLScheme scheme1,
410  unsigned short port2, EBURLScheme scheme2)
411 {
412  return port1 == port2
413  || ((!port1 || port1 == x_PortForScheme(0, scheme1)) &&
414  (!port2 || port2 == x_PortForScheme(0, scheme2)))
415  ? 1/*true*/ : 0/*false*/;
416 }
417 
418 
419 /* NB: treatment of 'host_from' and 'host_to' is not symmetrical */
420 static int/*bool*/ x_RedirectOK(EBURLScheme scheme_to,
421  const char* host_to,
422  unsigned short port_to,
423  EBURLScheme scheme_from,
424  const char* host_from,
425  unsigned short port_from)
426 {
427  char buf1[CONN_HOST_LEN+1], buf2[CONN_HOST_LEN+1];
428  unsigned int ip1, ip2;
429  if (!x_SameOrDefPort(port_to, scheme_to, port_from, scheme_from))
430  return 0/*false*/;
431  if (strcasecmp(host_to, host_from) == 0)
432  return 1/*true*/;
433  if (!SOCK_isipEx(host_from, 1/*full-quad*/))
434  return 0/*false*/;
435  if ((ip1 = SOCK_gethostbyname(host_from)) == (unsigned int)(-1))
436  ip1 = 0;
437  if (!ip1 || !SOCK_gethostbyaddr(ip1, buf1, sizeof(buf1)))
438  strncpy0(buf1, host_from, sizeof(buf1) - 1);
439  else if (strcasecmp(buf1, host_to) == 0)
440  return 1/*true*/;
441  if ((ip2 = SOCK_gethostbyname(host_to)) == (unsigned int)(-1))
442  ip2 = 0;
443  if (ip1/*&& ip2*/ && ip1 == ip2)
444  return 1/*true*/;
445  if (!ip2 || !SOCK_gethostbyaddr(ip2, buf2, sizeof(buf2)))
446  strncpy0(buf2, host_to, sizeof(buf2) - 1);
447  return strcasecmp(buf1, buf2) == 0 ? 1/*true*/ : 0/*false*/;
448 }
449 
450 
451 #ifdef __GNUC__
452 inline
453 #endif /*__GNUC__*/
454 static int/*bool*/ x_IsWriteThru(const SHttpConnector* uuu)
455 {
456  return !uuu->net_info->http_version || !(uuu->flags & fHTTP_WriteThru)
457  ? 0/*false*/ : 1/*true*/;
458 }
459 
460 
461 typedef enum {
468 
469 
470 static EHTTP_Redirect x_Redirect(SHttpConnector* uuu, const SRetry* retry)
471 {
472  EReqMethod req_method = (EReqMethod) uuu->net_info->req_method;
473  EBURLScheme scheme = uuu->net_info->scheme;
474  char host[sizeof(uuu->net_info->host)];
475  unsigned short port = uuu->net_info->port;
476  int/*bool*/ unsafe;
477 
478  strcpy(host, uuu->net_info->host);
479  if (req_method == eReqMethod_Any)
480  req_method = BUF_Size(uuu->w_buf) ? eReqMethod_Post : eReqMethod_Get;
481  ConnNetInfo_SetArgs(uuu->net_info, ""); /*arguments not inherited*/
482 
483  if (!ConnNetInfo_ParseURL(uuu->net_info, retry->data))
484  return eHTTP_RedirectError;
485 
486  unsafe = scheme == eURL_Https && uuu->net_info->scheme != eURL_Https
487  ? 1/*true*/ : 0/*false*/;
488 
489  if (req_method == eReqMethod_Put ||
490  req_method == eReqMethod_Post ||
491  req_method == eReqMethod_Delete) {
492  if (uuu->net_info->req_method == eReqMethod_Post
493  && retry->mode == eRetry_Redirect303) {
495  BUF_Erase(uuu->w_buf);
496  } else {
497  if (x_IsWriteThru(uuu) && BUF_Size(uuu->w_buf))
498  return eHTTP_RedirectInvalid;
499  if (!unsafe && !x_RedirectOK(uuu->net_info->scheme,
500  uuu->net_info->host,
501  uuu->net_info->port,
502  scheme,
503  host,
504  port)) {
505  unsafe = 1/*true*/;
506  }
507  }
508  }
509 
510  if (unsafe && !x_UnsafeRedirectOK(uuu))
511  return eHTTP_RedirectUnsafe;
512 
513  if ((uuu->flags & fHTTP_AdjustOnRedirect) && uuu->adjust) {
514  if (!s_CallAdjust(uuu, 0))
515  return eHTTP_RedirectError;
516  } else if (uuu->vhost
517  && (!x_SamePort(uuu->net_info->port,
518  uuu->net_info->scheme,
519  port,
520  scheme) ||
521  strcasecmp(uuu->net_info->host, host) != 0)) {
522  free((void*) uuu->vhost);
523  uuu->vhost = 0;
524  }
525 
526  return eHTTP_RedirectOK;
527 }
528 
529 
530 /* Try to fix connection parameters (called for an unconnected connector) */
532  const SRetry* retry,
533  EIO_Status status,
534  EExtractMode extract)
535 {
536  const char* msg;
537 
538  assert(status != eIO_Success);
539  assert(!retry || !retry->data || *retry->data);
540  assert(!uuu->sock && uuu->can_connect != fCC_None);
541 
542  uuu->retry = 0;
543  if (uuu->reused) {
544  assert(!uuu->sock && (!retry || !retry->data));
545  if (uuu->entity)
546  return eIO_Unknown;
547  uuu->retry = 1;
548  return eIO_Success;
549  }
550  if (!retry || !retry->mode || uuu->minor_fault > 5) {
551  uuu->minor_fault = 0;
552  uuu->major_fault++;
553  } else
554  uuu->minor_fault++;
555 
556  if (uuu->major_fault >= uuu->net_info->max_try) {
557  msg = extract != eEM_Drop && uuu->major_fault > 1
558  ? "[HTTP%s%s] Too many failed attempts (%hu), giving up" : "";
559  } else if (retry && retry->mode) {
560  int secure = uuu->net_info->scheme == eURL_Https ? 1/*T*/ : 0/*F*/;
561  char* url = ConnNetInfo_URL(uuu->net_info);
562  int fail = 0;
563  switch (retry->mode) {
564  case eRetry_Redirect:
565  if (uuu->entity/*FIXME*/)
566  fail = eHTTP_RedirectInvalid;
567  /*FALLTHRU*/
568  case eRetry_Redirect303:
569  if (!fail) {
571  fail = eHTTP_RedirectTunnel;
572  else if (!retry->data || *retry->data == '?')
573  fail = eHTTP_RedirectError;
574  else
575  fail = x_Redirect(uuu, retry);
576  }
577  if (fail) {
578  const char* reason;
579  switch (fail) {
581  reason = "Invalid";
582  status = eIO_Unknown;
583  break;
585  reason = "Prohibited";
586  status = eIO_NotSupported;
587  break;
588  case eHTTP_RedirectError:
589  reason = "Cannot";
590  status = eIO_Unknown;
591  break;
593  reason = "Spurious tunnel";
594  status = eIO_InvalidArg;
595  break;
596  default:
597  reason = "Unknown failure of";
598  status = eIO_Unknown;
599  assert(0);
600  break;
601  }
602  assert(status != eIO_Success);
604  ("[HTTP%s%s] %s %s%s to %s%s%s",
605  url ? "; " : "",
606  url ? url : "",
607  reason,
608  fail == eHTTP_RedirectUnsafe && secure
609  ? "insecure " : "",
610  fail > eHTTP_RedirectError
611  || retry->mode != eRetry_Redirect303
612  ? "redirect" : "submission",
613  retry->data ? "\"" : "<",
614  retry->data ? retry->data : "NULL",
615  retry->data ? "\"" : ">"));
616  } else {
618  ("[HTTP%s%s] %s \"%s\"",
619  url ? "; " : "",
620  url ? url : "",
621  retry->mode == eRetry_Redirect303
622  ? "Finishing submission with" : "Redirecting to",
623  retry->data));
624  status = eIO_Success;
625  }
626  break;
627  case eRetry_Authenticate:
629  fail = eHTTP_AuthConnect;
630  /*FALLTHRU*/
632  if (!fail) {
633  if (!retry->data
634  || strncasecmp(retry->data, "basic",
635  strcspn(retry->data, " \t")) != 0) {
636  fail = eHTTP_AuthNotSupp;
637  } else
638  fail = x_Authenticate(uuu, retry->mode, 1/*retry*/);
639  }
640  if (fail) {
641  const char* reason;
642  switch (fail) {
643  case eHTTP_AuthConnect:
644  reason = "not allowed with CONNECT";
645  status = eIO_Unknown;
646  break;
647  case eHTTP_AuthNotSupp:
648  reason = "not implemented";
649  status = eIO_NotSupported;
650  break;
651  case eHTTP_AuthIllegal:
652  reason = "cannot be done at this point";
653  status = eIO_InvalidArg;
654  break;
655  case eHTTP_AuthDone:
656  case eHTTP_AuthError:
657  reason = "failed";
658  status = eIO_Unknown;
659  break;
660  case eHTTP_AuthUnsafe:
661  reason = "prohibited";
662  status = eIO_NotSupported;
663  break;
664  case eHTTP_AuthMissing:
665  reason = "required";
666  status = eIO_Unknown;
667  break;
668  default:
669  reason = "unknown failure";
670  status = eIO_NotSupported;
671  assert(0);
672  break;
673  }
674  assert(status != eIO_Success);
676  ("[HTTP%s%s] %s %s %c%s%c",
677  url ? "; " : "",
678  url ? url : "",
679  retry->mode == eRetry_Authenticate
680  ? "Authorization" : "Proxy authorization",
681  reason,
682  "(["[!retry->data],
683  retry->data ? retry->data : "NULL",
684  ")]"[!retry->data]));
685  } else {
687  ("[HTTP%s%s] Authorizing%s",
688  url ? "; " : "",
689  url ? url : "",
690  retry->mode == eRetry_Authenticate
691  ? "" : " proxy"));
692  status = eIO_Success;
693  }
694  break;
695  default:
697  ("[HTTP%s%s] Unknown retry mode #%u",
698  url ? "; " : "",
699  url ? url : "", (unsigned int) retry->mode));
700  status = eIO_InvalidArg;
701  assert(0);
702  break;
703  }
704  if (url)
705  free(url);
706  if (status != eIO_Success)
707  uuu->can_connect = fCC_None;
708  return status;
709  } else if (uuu->adjust && !s_CallAdjust(uuu, uuu->major_fault)) {
710  msg = extract != eEM_Drop && uuu->major_fault > 1
711  ? "[HTTP%s%s] Retry attempts (%hu) exhausted, giving up" : "";
712  } else
713  return eIO_Success;
714 
715  assert(msg);
716  if (*msg) {
717  char* url = ConnNetInfo_URL(uuu->net_info);
719  (msg,
720  url ? "; " : "",
721  url ? url : "", uuu->major_fault));
722  if (url)
723  free(url);
724  }
725  uuu->can_connect = fCC_None;
726  return status;
727 }
728 
729 
730 static char* x_HostPort(size_t reserve, const char* host, unsigned short xport)
731 {
732  size_t hostlen = strlen(host), portlen;
733  char* hostport, port[16];
734 
735  if (!xport) {
736  portlen = 1;
737  port[0] = '\0';
738  } else
739  portlen = (size_t) sprintf(port, ":%hu", xport) + 1;
740  hostport = (char*) malloc(reserve + hostlen + portlen);
741  if (hostport) {
742  memcpy(hostport + reserve, host, hostlen);
743  hostlen += reserve;
744  memcpy(hostport + hostlen, port, portlen);
745  }
746  return hostport;
747 }
748 
749 
750 static const char* x_SetHttpHostTag(SConnNetInfo* net_info)
751 {
752  char* tag;
753  unsigned short port = net_info->port;
754 
755  if (port && port == x_PortForScheme(0, net_info->scheme))
756  port = 0;
757  tag = x_HostPort(sizeof(kHttpHostTag)-1, net_info->host, port);
758  if (tag) {
759  memcpy(tag, kHttpHostTag, sizeof(kHttpHostTag)-1);
760  if (ConnNetInfo_PreOverrideUserHeader(net_info, tag)) {
761  char* v = tag + sizeof(kHttpHostTag)-1;
762  char* c = strchr(v, ':');
763  size_t s = c ? (size_t)(c - v) : strlen(v);
764  memmove(tag, v, s);
765  tag[s] = '\0';
766  } else {
767  free(tag);
768  tag = 0;
769  }
770  }
771  return tag;
772 }
773 
774 
775 static void x_SetRequestIDs(SConnNetInfo* net_info)
776 {
778  assert(!id || *id);
779  if (id) {
780  char* tag;
781  size_t len = strlen(id);
782  if (!(tag = (char*) realloc(id, ++len + sizeof(HTTP_NCBI_SID)))) {
784  free(id);
785  } else {
786  memmove(tag + sizeof(HTTP_NCBI_SID), tag, len);
787  memcpy (tag, HTTP_NCBI_SID, sizeof(HTTP_NCBI_SID) - 1);
788  tag[sizeof(HTTP_NCBI_SID) - 1] = ' ';
790  free(tag);
791  }
792  }
794  assert(!id || *id);
795  if (id) {
796  char* tag;
797  size_t len = strlen(id);
798  if (!(tag = (char*) realloc(id, ++len + sizeof(HTTP_NCBI_PHID)))) {
800  free(id);
801  } else {
802  memmove(tag + sizeof(HTTP_NCBI_PHID), tag, len);
803  memcpy (tag, HTTP_NCBI_PHID, sizeof(HTTP_NCBI_PHID) - 1);
804  tag[sizeof(HTTP_NCBI_PHID) - 1] = ' ';
806  free(tag);
807  }
808  }
809 }
810 
811 
812 /*ARGSUSED*/
813 static int s_TunnelAdjust(SConnNetInfo* net_info, void* data, unsigned int arg)
814 {
816  uuu->minor_fault = 0;
817  uuu->major_fault++;
818  return -1/*noop*/;
819 }
820 
821 
822 /* Connect to the HTTP server, specified by uuu->net_info's "port:host".
823  * Return eIO_Success only if socket connection has succeeded and uuu->sock
824  * is non-zero. If unsuccessful, try to adjust uuu->net_info with s_Adjust(),
825  * and then re-try the connection attempt.
826  */
828  const STimeout* timeout,
829  EExtractMode extract)
830 {
831  EIO_Status status;
832  SOCK sock;
833 
835 
836  uuu->http_code = 0;
837  if (!(uuu->can_connect & fCC_Once)) {
838  if (extract == eEM_Read && uuu->net_info->max_try
839  && uuu->can_connect == fCC_None) {
840  char* url = ConnNetInfo_URL(uuu->net_info);
842  ("[HTTP%s%s] Connector is no longer usable",
843  url ? "; " : "",
844  url ? url : ""));
845  if (url)
846  free(url);
847  }
848  return eIO_Unknown;
849  }
850 
851  if (uuu->conn_state == eCS_Eom) {
852  if (uuu->adjust) {
853  int retval = s_CallAdjust(uuu, (unsigned int)(-1));
854  if (!retval)
855  return eIO_Unknown;
856  if (retval < 0)
857  return eIO_Closed;
858  } else
859  return eIO_Closed;
861  }
862 
863  uuu->entity = 0;
864  uuu->chunked = 0;
865  /* the re-try loop... */
866  for (;;) {
871  sock = uuu->sock;
872  uuu->sock = 0;
873  uuu->reused = sock ? 1/*true*/ : 0/*false*/;
874  if ((!sock || !SOCK_IsSecure(sock))
876  && uuu->net_info->scheme == eURL_Https
877  && uuu->net_info->http_proxy_host[0]
878  && uuu->net_info->http_proxy_port
879  && uuu->net_info->http_proxy_mask != fProxy_Http) {
880  SConnNetInfo* net_info = ConnNetInfo_Clone(uuu->net_info);
881  uuu->reused = 0/*false*/;
882  if (!net_info) {
883  status = eIO_Unknown;
884  break;
885  }
886  net_info->user[0] = '\0';
887  net_info->pass[0] = '\0';
888  if (net_info->port == 0)
889  net_info->port = CONN_PORT_HTTPS;
890  net_info->firewall = 0/*false*/;
891  status = HTTP_CreateTunnelEx(net_info, fHTTP_NoUpread,
892  0, 0, uuu, s_TunnelAdjust, &sock);
893  if (status != eIO_Success && sock)
894  break;
895  ConnNetInfo_Destroy(net_info);
896  } else
897  status = eIO_Success;
898  if (status == eIO_Success) {
899  EReqMethod req_method = (EReqMethod) uuu->net_info->req_method;
900  int/*bool*/ reset_user_header;
901  char* http_user_header;
902  SURLExtra extra, *extrap;
903  const char* host;
904  unsigned short port;
905  const char* path;
906  const char* args;
907  char* temp;
908  size_t len;
909 
910  /* RFC7230 now requires Host: for CONNECT just as well */
911  if (!uuu->vhost
912  && !(uuu->vhost = x_SetHttpHostTag(uuu->net_info))) {
913  status = eIO_Unknown;
914  break;
915  }
916  if (x_IsWriteThru(uuu)) {
917  assert(req_method != eReqMethod_Connect);
918  if (req_method == eReqMethod_Any) {
919  req_method = BUF_Size(uuu->w_buf)
921  : eReqMethod_Get;
922  }
923  if (req_method != eReqMethod_Head &&
924  req_method != eReqMethod_Get) {
926  (uuu->net_info, "Transfer-Encoding: chunked")) {
927  status = eIO_Unknown;
928  break;
929  }
930  uuu->chunked = 1;
931  }
932  len = (size_t)(-1L);
933  } else
934  len = BUF_Size(uuu->w_buf);
935  if (req_method == eReqMethod_Connect
936  || (uuu->net_info->scheme != eURL_Https
937  && uuu->net_info->http_proxy_host[0]
938  && uuu->net_info->http_proxy_port
939  && uuu->net_info->http_proxy_mask != fProxy_Https)) {
940  if (uuu->net_info->http_push_auth &&
942  || (req_method != eReqMethod_Connect &&
943  x_Authenticate(uuu, eRetry_Authenticate, 0) > 0))) {
944  status = eIO_Unknown;
945  break;
946  }
947  host = uuu->net_info->http_proxy_host;
948  port = uuu->net_info->http_proxy_port;
949  path = ConnNetInfo_URL(uuu->net_info);
950  if (!path) {
951  status = eIO_Unknown;
952  break;
953  }
954  if (req_method == eReqMethod_Connect) {
955  /* Tunnel (RFC2817) */
956  assert(!uuu->net_info->http_version);
957  if (!len) {
958  args = 0;
959  } else if (!(temp = (char*) malloc(len))
960  || BUF_Peek(uuu->w_buf, temp, len) != len) {
961  if (temp)
962  free(temp);
963  status = eIO_Unknown;
964  free((void*) path);
965  break;
966  } else
967  args = temp;
968  } else {
969  /* Proxied HTTP */
970  assert(uuu->net_info->scheme == eURL_Http);
971  if (uuu->flags & fHCC_UrlEncodeArgs) {
972  /* args added to path not valid(unencoded), remove */
973  if ((temp = (char*) strchr(path, '?')) != 0)
974  *temp = '\0';
975  args = ConnNetInfo_GetArgs(uuu->net_info);
976  if (args && (!*args || *args == '#'))
977  args = 0;
978  } else
979  args = 0;
980  }
981  } else {
982  /* Direct HTTP[S] or tunneled HTTPS */
983  if (uuu->net_info->http_push_auth &&
984  x_Authenticate(uuu, eRetry_Authenticate, 0) > 0) {
985  status = eIO_Unknown;
986  break;
987  }
988  host = uuu->net_info->host;
989  port = uuu->net_info->port;
990  args = ConnNetInfo_GetArgs(uuu->net_info);
991  if (*args == '#' || !(uuu->flags & fHCC_UrlEncodeArgs)) {
992  path = uuu->net_info->path;
993  args = 0;
994  } else if (!(path = strndup(uuu->net_info->path, (size_t)
995  (args - uuu->net_info->path) -
996  !(args == uuu->net_info->path)
997  /*'?'*/))) {
998  status = eIO_Unknown;
999  break;
1000  } else if (!*args)
1001  args = 0;
1002  }
1003 
1004  /* encode args (obsolete feature) */
1005  if (req_method != eReqMethod_Connect && args) {
1006  size_t args_len = strcspn(args, "#");
1007  size_t size = args_len * 3;
1008  size_t rd_len, wr_len;
1009  assert((uuu->flags & fHCC_UrlEncodeArgs) && args_len > 0);
1010  if (!(temp = (char*) malloc(size + strlen(args+args_len) +1))){
1011  int error = errno;
1012  temp = ConnNetInfo_URL(uuu->net_info);
1014  ("[HTTP%s%s] Out of memory encoding"
1015  " URL arguments (%lu bytes)",
1016  temp ? "; " : "",
1017  temp ? temp : "",
1018  (unsigned long)(size + strlen
1019  (args + args_len) +1)));
1020  if (path != uuu->net_info->path)
1021  free((void*) path);
1022  status = eIO_Unknown;
1023  break;
1024  }
1025  URL_Encode(args, args_len, &rd_len, temp, size, &wr_len);
1026  assert(rd_len == args_len);
1027  assert(wr_len <= size);
1028  strcpy(temp + wr_len, args + args_len);
1029  args = temp;
1030  }
1031 
1032  /* NCBI request IDs */
1033  if (!(uuu->flags & fHTTP_NoAutomagicSID))
1034  x_SetRequestIDs(uuu->net_info);
1035 
1036  /* identify the connector in the User-Agent: header tag */
1037  http_user_header = (uuu->net_info->http_user_header
1039  : 0);
1040  if (!uuu->net_info->http_user_header == !http_user_header) {
1042  (uuu->net_info, "User-Agent: NCBIHttpConnector"
1043 #ifdef NCBI_CXX_TOOLKIT
1044  " (CXX Toolkit)"
1045 #else
1046  " (C Toolkit)"
1047 #endif /*NCBI_CXX_TOOLKIT*/
1048  );
1049  if (!uuu->net_info->http_version
1050  && req_method != eReqMethod_Connect) {
1052  "Connection: keep-alive");
1053  }
1054  reset_user_header = 1;
1055  } else
1056  reset_user_header = 0;
1057 
1058  if (uuu->net_info->debug_printout) {
1061  uuu->retry ? eLOG_Trace : eLOG_Note,
1062  CORE_GetLOG());
1063  CORE_UNLOCK;
1064  }
1065  uuu->retry = 0;
1066 
1067  /* connect & send HTTP header */
1069  && uuu->net_info->scheme == eURL_Https) {
1070  memset(&extra, 0, sizeof(extra));
1071  extra.cred = uuu->net_info->credentials;
1072  extra.host = uuu->vhost;
1073  flags |= fSOCK_Secure;
1074  extrap = &extra;
1075  } else
1076  extrap = 0;
1077  if (!(uuu->flags & fHTTP_NoUpread))
1079 
1080  status = URL_ConnectEx(host, port, path, args,
1081  req_method | (uuu->net_info->http_version
1082  ? eReqMethod_v1
1083  : 0), len,
1084  uuu->o_timeout, timeout,
1085  uuu->net_info->http_user_header,
1086  extrap, flags, &sock);
1087  /* FIXME: remember if "sock" reused here; and not cause major fault
1088  * if failed later; but simply re-try w/o calling adjust. */
1089 
1090  if (reset_user_header) {
1092  uuu->net_info->http_user_header = http_user_header;
1093  }
1094 
1095  if (path != uuu->net_info->path)
1096  free((void*) path);
1097  if (args)
1098  free((void*) args);
1099 
1100  if (sock) {
1101  assert(status == eIO_Success);
1102  uuu->w_len = req_method != eReqMethod_Connect
1103  ? BUF_Size(uuu->w_buf) : 0;
1104  break;
1105  }
1106  } else
1107  assert(!sock);
1108 
1109  assert(status != eIO_Success);
1110  /* connection failed, try another server */
1111  if ((status = s_Adjust(uuu, 0, status, extract)) != eIO_Success)
1112  break;
1113  }
1114 
1115  if (status == eIO_Success) {
1117  uuu->sock = sock;
1118  assert(uuu->sock);
1119  } else {
1120  if (sock) {
1121  SOCK_Abort(sock);
1122  SOCK_Destroy(sock);
1123  }
1124  assert(!uuu->sock);
1125  if (status == eIO_Closed)
1126  status = eIO_Unknown;
1127  }
1128  return status;
1129 }
1130 
1131 
1132 /* Unconditionally drop the connection w/o any wait */
1134 {
1135  assert(uuu->sock);
1136  if (!(uuu->conn_state & eCS_ReadBody) || uuu->conn_state == eCS_Discard)
1137  SOCK_Abort(uuu->sock);
1138  else
1140  SOCK_Close(uuu->sock);
1141  uuu->sock = 0;
1142  uuu->retry = 0;
1143  uuu->conn_state = state;
1144 }
1145 
1146 
1147 typedef struct {
1150 } XBUF_PeekCBCtx;
1151 
1152 
1153 static size_t x_WriteBuf(void* data, const void* buf, size_t size)
1154 {
1156  size_t written;
1157 
1158  assert(buf && size);
1159  assert(ctx->status == eIO_Success);
1160  ctx->status = SOCK_Write(ctx->sock, buf, size,
1161  &written, eIO_WritePlain);
1162  return written;
1163 }
1164 
1165 
1166 /* Connect to the server specified by uuu->net_info, then compose and form
1167  * relevant HTTP header, and flush the accumulated output data(uuu->w_buf)
1168  * after the HTTP header.
1169  * If connection/write unsuccessful, retry to reconnect and send the data
1170  * again until permitted by s_Adjust().
1171  */
1173  const STimeout* timeout,
1174  EExtractMode extract)
1175 {
1176  EIO_Status status = eIO_Success;
1177 
1178  for (;;) {
1179  char what[80];
1180  int error;
1181  size_t off;
1182  char* url;
1183 
1184  assert(status == eIO_Success);
1185 
1186  if ((uuu->conn_state == eCS_NotInitiated || uuu->conn_state == eCS_Eom)
1187  && (status = s_Connect(uuu, timeout, extract)) != eIO_Success) {
1188  break;
1189  }
1190 
1191  if (uuu->w_len) {
1193  ctx.sock = uuu->sock;
1194  ctx.status = eIO_Success/*NB:==status*/;
1195  off = BUF_Size(uuu->w_buf) - uuu->w_len;
1197  SOCK_SetTimeout(ctx.sock, eIO_Write, uuu->w_timeout);
1198  uuu->w_len -= BUF_PeekAtCB(uuu->w_buf, off,
1199  x_WriteBuf, &ctx, uuu->w_len);
1200  status = ctx.status;
1201  } else
1202  off = 0;
1203 
1204  if (status == eIO_Success) {
1205  if (uuu->w_len)
1206  continue;
1207  if (uuu->conn_state > eCS_FlushRequest)
1208  break;
1209  if (!uuu->chunked)
1211  if (uuu->conn_state == eCS_FlushRequest) {
1212  /* "flush" */
1213  status = SOCK_Write(uuu->sock, 0, 0, 0, eIO_WritePlain);
1214  if (status == eIO_Success) {
1215  uuu->conn_state = eCS_ReadHeader;
1216  uuu->keepalive = uuu->net_info->http_version;
1217  uuu->expected = (TNCBI_BigCount)(-1L);
1218  uuu->received = 0;
1219  uuu->chunked = 0;
1220  BUF_Erase(uuu->http);
1221 #if !defined(NCBI_OS_MSWIN) && !defined(NCBI_OS_CYGWIN)
1222  if (!uuu->keepalive)
1223  ; //status = SOCK_Shutdown(uuu->sock, eIO_Write);
1224 #endif /*NCBI_OS_MSWIN*/
1225  break;
1226  }
1227  } else
1228  break;
1229  }
1230 
1231  if (status == eIO_Timeout
1232  && (extract == eEM_Wait
1233  || (timeout && !(timeout->sec | timeout->usec)))) {
1234  break;
1235  }
1236 
1237  error = errno;
1238  if (uuu->w_len && uuu->conn_state == eCS_WriteRequest) {
1239  if (!x_IsWriteThru(uuu)) {
1240  sprintf(what, "write request body at offset %lu",
1241  (unsigned long) off);
1242  } else
1243  strcpy(what, "write request body");
1244  } else {
1245  if (uuu->w_len)
1246  strcpy(what, "finalize request body");
1247  else
1248  strcpy(what, "finalize request");
1249  }
1250 
1251  url = ConnNetInfo_URL(uuu->net_info);
1253  ("[HTTP%s%s] Cannot %s (%s)",
1254  url ? "; " : "",
1255  url ? url : "", what, IO_StatusStr(status)));
1256  if (url)
1257  free(url);
1258 
1259  /* write failed; close and try to use another server, if possible */
1261  assert(status != eIO_Success);
1262  if ((status = s_Adjust(uuu, 0, status, extract)) != eIO_Success)
1263  break;
1264  }
1265 
1266  return status;
1267 }
1268 
1269 
1270 static int/*bool*/ x_IsValidParam(const char* param, size_t paramlen)
1271 {
1272  const char* e = (const char*) memchr(param, '=', paramlen);
1273  size_t len;
1274  if (!e || e == param)
1275  return 0/*false*/;
1276  if ((len = (size_t)(++e - param)) >= paramlen)
1277  return 0/*false*/;
1278  assert(!isspace((unsigned char)(*param)));
1279  if (strcspn(param, " \t") < len)
1280  return 0/*false*/;
1281  if (*e == '\'' || *e == '"') {
1282  /* a quoted string */
1283  assert(len < paramlen);
1284  len = paramlen - len;
1285  if (!(e = (const char*) memchr(e + 1, *e, --len)))
1286  return 0/*false*/;
1287  e++/*skip the quote*/;
1288  } else
1289  e += strcspn(e, " \t");
1290  if (e != param + paramlen && e + strspn(e, " \t") != param + paramlen)
1291  return 0/*false*/;
1292  return 1/*true*/;
1293 }
1294 
1295 
1296 static int/*bool*/ x_IsValidChallenge(const char* text, size_t len)
1297 {
1298  /* Challenge must contain a scheme name token and a non-empty param
1299  * list (comma-separated pairs token={token|quoted_string}), with at
1300  * least one parameter being named "realm=".
1301  */
1302  size_t word = strcspn(text, " \t");
1303  int retval = 0/*false*/;
1304  if (word < len) {
1305  /* 1st word is always the scheme name */
1306  const char* param = text + word;
1307  for (param += strspn(param, " \t"); param < text + len;
1308  param += strspn(param, ", \t")) {
1309  size_t paramlen = (size_t)(text + len - param);
1310  const char* c = (const char*) memchr(param, ',', paramlen);
1311  if (c)
1312  paramlen = (size_t)(c - param);
1313  if (!x_IsValidParam(param, paramlen))
1314  return 0/*false*/;
1315  if (paramlen > 6 && strncasecmp(param, "realm=", 6) == 0)
1316  retval = 1/*true, but keep scanning*/;
1317  param += c ? ++paramlen : paramlen;
1318  }
1319  }
1320  return retval;
1321 }
1322 
1323 
1324 static size_t x_PushbackBuf(void* data, const void* buf, size_t size)
1325 {
1327 
1328  assert(buf && size);
1329  assert(ctx->status == eIO_Success);
1330  ctx->status = SOCK_Pushback(ctx->sock, buf, size);
1331  return ctx->status != eIO_Success ? 0 : size;
1332 }
1333 
1334 
1335 static int/*bool*/ x_Pushback(SOCK sock, BUF buf)
1336 {
1337  size_t size = BUF_Size(buf);
1339  ctx.sock = sock;
1340  ctx.status = eIO_Success;
1341  return !(size ^ BUF_PeekAtCB(buf, 0, x_PushbackBuf, &ctx, size));
1342 }
1343 
1344 
1346 {
1347  int n;
1348  BUF buf;
1349  char* str;
1350  size_t size;
1351  TNCBI_BigCount chunk;
1352  EIO_Status status;
1353 
1354  buf = 0;
1355  str = 0;
1356  size = 0;
1357  for (;;) {
1358  size_t off;
1359  status = SOCK_StripToPattern(uuu->sock, "\r\n", 2, &buf, &off);
1360  if (status != eIO_Success || (size += off) != BUF_Size(buf))
1361  break;
1362  if (size > 2) {
1363  if (!(str = (char*) malloc(size + 1)))
1364  break;
1365  verify(BUF_Peek(buf, str, size) == size);
1366  if (!first && (str[0] != '\r' || str[1] != '\n')) {
1367  status = eIO_NotSupported/*protocol error*/;
1368  free(str);
1369  str = 0;
1370  break;
1371  }
1372  assert(str[size - 1] == '\n');
1373  str[size] = '\0';
1374  break;
1375  }
1376  }
1377 
1378  if (!str
1379  || sscanf(str, "%" NCBI_BIGCOUNT_FORMAT_SPEC_HEX "%n", &chunk, &n) < 1
1380  || (!isspace((unsigned char) str[n]) && str[n] != ';')) {
1381  int error = errno/*iff memory allocation failure*/;
1382  char* url = ConnNetInfo_URL(uuu->net_info);
1383  const char* err;
1384  char errbuf[256];
1385  if (!str) {
1386  if (status == eIO_Success) {
1387  if (BUF_Size(buf) != size) {
1388  sprintf(errbuf, "Partial read %lu out of %lu",
1389  (unsigned long) BUF_Size(buf),
1390  (unsigned long) size);
1391  err = errbuf;
1392  } else
1393  err = strerror(error);
1394  } else {
1395  err = status != eIO_NotSupported
1396  ? IO_StatusStr(status)
1397  : "Protocol error";
1398  }
1399  } /* else "err" is unused */
1400  CORE_LOGF_X(23, eLOG_Error,
1401  ("[HTTP%s%s] Cannot read chunk size: %s%.*s%s",
1402  url ? "; " : "",
1403  url ? url : "",
1404  &"\""[!str],
1405  !str ? (int) strlen(err) : (int)(size - (first ? 2 : 4)),
1406  !str ? err : str + (first ? 0 : 2),
1407  &"\""[!str]));
1408  if (url)
1409  free(url);
1410  if (str)
1411  free(str);
1412  if (status == eIO_Closed || !x_Pushback(uuu->sock, buf))
1413  status = eIO_Unknown;
1414  BUF_Destroy(buf);
1415  return status ? status : eIO_Unknown;
1416  }
1417 
1418  free(str);
1419  BUF_Destroy(buf);
1420  uuu->received = 0;
1421  uuu->expected = chunk;
1422  return eIO_Success;
1423 }
1424 
1425 
1427 {
1428  EIO_Status status;
1429  BUF buf = 0;
1430  do {
1431  size_t n;
1432  status = SOCK_StripToPattern(uuu->sock, "\r\n", 2, &buf, &n);
1433  if (n == 2) {
1434  /*last trailer*/
1435  BUF_Destroy(buf);
1436  uuu->conn_state = eCS_Discard;
1437  return eIO_Closed;
1438  }
1439  } while (status == eIO_Success);
1440  if (status == eIO_Closed) {
1441  char* url = ConnNetInfo_URL(uuu->net_info);
1442  CORE_LOGF_X(25, eLOG_Error,
1443  ("[HTTP%s%s] Cannot read chunk tail",
1444  url ? "; " : "",
1445  url ? url : ""));
1446  if (url)
1447  free(url);
1448  status = eIO_Unknown;
1449  } else if (!x_Pushback(uuu->sock, buf))
1450  status = eIO_Unknown;
1451  BUF_Destroy(buf);
1452  return status;
1453 }
1454 
1455 
1456 /* If "size"==0, then "buf" is a pointer to a BUF to receive the current body.
1457  * Otherwise, "buf" is a non-NULL destination buffer.
1458  */
1460  void* buf, size_t size,
1461  size_t* n_read, EIO_ReadMethod how)
1462 {
1463  BUF* xxx;
1464  EIO_Status status;
1465 
1466  assert(buf && n_read && !*n_read && uuu->conn_state == eCS_ReadBody);
1467 
1468  if (!uuu->chunked || uuu->expected > uuu->received) {
1469  if (!size) {
1470  size = uuu->expected != (TNCBI_BigCount)(-1L)
1471  ? uuu->expected - uuu->received
1473  if (!size) {
1474  assert(!uuu->chunked);
1475  uuu->conn_state = eCS_DoneBody;
1476  return eIO_Closed;
1477  }
1478  if (size > HTTP_SOAK_READ_SIZE)
1480  xxx = (BUF*) buf;
1481  if (!(buf = (void*) malloc(size))) {
1482  int error = errno;
1483  char* url = ConnNetInfo_URL(uuu->net_info);
1485  ("[HTTP%s%s] Cannot allocate response chunk"
1486  " (%lu byte%s)",
1487  url ? "; " : "",
1488  url ? url : "",
1489  (unsigned long) size, &"s"[size == 1]));
1490  if (url)
1491  free(url);
1492  return eIO_Unknown;
1493  }
1494  } else if (uuu->chunked) {
1495  assert(uuu->expected != (TNCBI_BigCount)(-1L));
1496  if (size > uuu->expected - uuu->received)
1497  size = uuu->expected - uuu->received;
1498  xxx = 0;
1499  } else if (uuu->expected == uuu->received) {
1500  uuu->conn_state = eCS_DoneBody;
1501  return eIO_Closed;
1502  } else
1503  xxx = 0;
1504  status = SOCK_Read(uuu->sock, buf, size, n_read, how);
1505  if (xxx && !BUF_AppendEx(xxx, buf, size, buf, *n_read)) {
1506  int error = errno;
1507  char* url = ConnNetInfo_URL(uuu->net_info);
1509  ("[HTTP%s%s] Cannot collect response body",
1510  url ? "; " : "",
1511  url ? url : ""));
1512  if (url)
1513  free(url);
1514  free(buf);
1515  status = eIO_Unknown;
1516  }
1517  if (status == eIO_Closed)
1518  uuu->conn_state = eCS_Eom;
1519  return status;
1520  }
1521 
1522  if ((status = x_ReadChunkHead(uuu, !uuu->expected)) != eIO_Success)
1523  return status;
1524 
1525  if (uuu->expected) {
1526  assert(uuu->expected != (TNCBI_BigCount)(-1L) && !uuu->received);
1527  if (size > uuu->expected)
1528  size = uuu->expected;
1529  return s_ReadData(uuu, buf, size, n_read, how);
1530  }
1531 
1532  uuu->conn_state = eCS_DoneBody;
1533  return x_ReadChunkTail(uuu);
1534 }
1535 
1536 
1537 static int/*bool*/ x_ErrorHeaderOnly(SHttpConnector* uuu)
1538 {
1539  if (uuu->error_header == eDefault) {
1540  char val[32];
1542  "HTTP_ERROR_HEADER_ONLY",
1543  val, sizeof(val), 0);
1545  }
1546  return uuu->error_header == eOn ? 1/*true*/ : 0/*false*/;
1547 }
1548 
1549 
1550 /* Read and parse HTTP header */
1552  SRetry* retry,
1553  EExtractMode extract)
1554 {
1555  enum EHTTP_Tag {
1556  eHTTP_NcbiMsg = 1 << 0,
1557  eHTTP_NcbiSid = 1 << 1,
1558  eHTTP_Location = 1 << 2,
1559  eHTTP_Connection = 1 << 3,
1560  eHTTP_Authenticate = 1 << 4,
1561  eHTTP_ContentLength = 1 << 5,
1562  eHTTP_TransferEncoding = 1 << 6
1563  };
1564  typedef unsigned short THTTP_Tags; /* Bitwise-OR of EHTTP_Tag */
1565  char* url = 0, *hdr, *s;
1566  EHTTP_HeaderParse header_parse;
1567  int http_code;
1568  size_t size, n;
1569  EIO_Status status;
1570  int fatal;
1571  THTTP_Tags tags;
1572 
1573  assert(uuu->sock && uuu->conn_state == eCS_ReadHeader);
1574  memset(retry, 0, sizeof(*retry));
1575  /*retry->mode = eRetry_None;
1576  retry->data = 0;*/
1577 
1578  /* line by line HTTP header input */
1579  for (;;) {
1580  /* do we have full header yet? */
1581  if ((size = BUF_Size(uuu->http)) >= 4) {
1582  if (!(hdr = (char*) malloc(size + 1))) {
1583  int error = errno;
1584  assert(!url);
1585  url = ConnNetInfo_URL(uuu->net_info);
1587  ("[HTTP%s%s] Cannot allocate header"
1588  " (%lu bytes)",
1589  url ? "; " : "",
1590  url ? url : "",
1591  (unsigned long) size));
1592  if (url)
1593  free(url);
1594  uuu->reused = 0;
1595  return eIO_Unknown;
1596  }
1597  verify(BUF_Peek(uuu->http, hdr, size) == size);
1598  if (memcmp(&hdr[size - 4], "\r\n\r\n", 4) == 0) {
1599  /* full header captured */
1600  hdr[size] = '\0';
1601  break;
1602  }
1603  free(hdr);
1604  }
1605 
1606  status = SOCK_StripToPattern(uuu->sock, "\r\n", 2, &uuu->http, &n);
1607 
1608  if (status != eIO_Success || size + n != BUF_Size(uuu->http)) {
1609  ELOG_Level level;
1610  if (status == eIO_Timeout) {
1611  const STimeout* tmo = SOCK_GetTimeout(uuu->sock, eIO_Read);
1612  if (!tmo)
1613  level = eLOG_Error;
1614  else if (extract == eEM_Wait)
1615  return status;
1616  else if (tmo->sec | tmo->usec)
1617  level = eLOG_Warning;
1618  else
1619  level = eLOG_Trace;
1620  } else
1621  level = status && uuu->reused ? eLOG_Trace : eLOG_Error;
1622  assert(!url);
1623  url = ConnNetInfo_URL(uuu->net_info);
1624  CORE_LOGF_X(8, level,
1625  ("[HTTP%s%s] Cannot %s header (%s)",
1626  url ? "; " : "",
1627  url ? url : "",
1628  status != eIO_Success ? "read" : "scan",
1629  IO_StatusStr(status != eIO_Success
1630  ? status : eIO_Unknown)));
1631  if (url)
1632  free(url);
1633  if (status != eIO_Success)
1634  return status;
1635  uuu->reused = 0;
1636  return eIO_Unknown;
1637  }
1638  }
1639  /* the entire header has been read in */
1640  uuu->conn_state = eCS_ReadBody;
1641  BUF_Erase(uuu->http);
1642  uuu->reused = 0;
1643  assert(hdr);
1644 
1645  /* HTTP status must come on the first line of the response */
1646  fatal = 0/*false*/;
1647  if (sscanf(hdr, "HTTP/%*d.%*d %d ", &http_code) != 1 || !http_code)
1648  http_code = -1;
1649  uuu->http_code = (unsigned short) http_code;
1650  if (http_code < 200 || 299 < http_code) {
1651  if (http_code == 304)
1652  /*void*/;
1653  else if (http_code == 301 || http_code == 302 || http_code == 307)
1654  retry->mode = eRetry_Redirect;
1655  else if (http_code == 303)
1656  retry->mode = eRetry_Redirect303;
1657  else if (http_code == 401)
1658  retry->mode = eRetry_Authenticate;
1659  else if (http_code == 407)
1660  retry->mode = eRetry_ProxyAuthenticate;
1661  else if (http_code <= 0 ||
1662  http_code == 400 || http_code == 403 ||
1663  http_code == 404 || http_code == 405 ||
1664  http_code == 406 || http_code == 410) {
1665  fatal = 1/*true*/;
1666  }
1667  } else
1668  http_code = 0/*no server error*/;
1669 
1671  && (http_code || !x_ErrorHeaderOnly(uuu))) {
1672  /* HTTP header gets printed as part of data logging when
1673  uuu->net_info->debug_printout == eDebugPrintout_Data. */
1674  const char* header_header;
1675  if (!http_code || http_code == 304)
1676  header_header = "HTTP header";
1677  else if (fatal)
1678  header_header = "HTTP header (fatal)";
1679  else if (uuu->flags & fHTTP_KeepHeader)
1680  header_header = "HTTP header (error)";
1681  else if (retry->mode == eRetry_Redirect)
1682  header_header = "HTTP header (redirect)";
1683  else if (retry->mode == eRetry_Redirect303)
1684  header_header = "HTTP header (see other)";
1685  else if (retry->mode & eRetry_Authenticate)
1686  header_header = "HTTP header (authentication)";
1687  else if (retry->mode)
1688  header_header = 0, assert(0);
1689  else
1690  header_header = "HTTP header (retriable server error)";
1691  assert(!url);
1692  url = ConnNetInfo_URL(uuu->net_info);
1694  ("[HTTP%s%s] %s",
1695  url ? "; " : "",
1696  url ? url : "", header_header));
1697  }
1698 
1699  if (!(uuu->flags & fHTTP_KeepHeader)) {
1700  if (fatal) {
1701  assert(http_code);
1702  if (!uuu->net_info->debug_printout && !uuu->parse_header) {
1703  char text[40];
1704  if (!url)
1705  url = ConnNetInfo_URL(uuu->net_info);
1706  if (http_code > 0)
1707  sprintf(text, "%d", http_code);
1708  else
1709  strcpy(text, "occurred");
1710  CORE_LOGF_X(22, eLOG_Error,
1711  ("[HTTP%s%s] Fatal error %s",
1712  url ? "; " : "",
1713  url ? url : "", text));
1714  }
1715  if (!uuu->adjust)
1716  uuu->net_info->max_try = 0;
1717  }
1718 
1719  header_parse = uuu->parse_header
1720  ? uuu->parse_header(hdr, uuu->user_data, http_code)
1722 
1723  if (header_parse == eHTTP_HeaderError) {
1724  retry->mode = eRetry_None;
1726  && !http_code/*i.e. was okay*/ && x_ErrorHeaderOnly(uuu)) {
1727  if (!url)
1728  url = ConnNetInfo_URL(uuu->net_info);
1729  CORE_DATAF_X(9, eLOG_Note, hdr, size,
1730  ("[HTTP%s%s] HTTP header (parse error)",
1731  url ? "; " : "",
1732  url ? url : ""));
1733  }
1734  } else if (header_parse == eHTTP_HeaderComplete) {
1735  /* i.e. stop processing */
1736  retry->mode = eRetry_None;
1737  http_code = 0/*fake success*/;
1738  }
1739  } else {
1740  header_parse = eHTTP_HeaderSuccess;
1741  retry->mode = eRetry_None;
1742  }
1743 
1744  tags = (THTTP_Tags)
1745  (eHTTP_NcbiMsg | eHTTP_Connection
1746  | (retry->mode & eRetry_Redirect ? eHTTP_Location :
1747  retry->mode & eRetry_Authenticate ? eHTTP_Authenticate : 0)
1748  | (uuu->flags & fHTTP_NoAutomagicSID ? 0 : eHTTP_NcbiSid));
1749  if (uuu->http_code / 100 != 1
1750  && uuu->http_code != 204 && uuu->http_code != 304) {
1751  tags |= eHTTP_ContentLength | eHTTP_TransferEncoding;
1752  }
1753 
1754  /* NB: the loop may clobber "hdr" unless fHTTP_KeepHeader is set */
1755  for (s = strchr(hdr, '\n'); s && *s; s = strchr(s + 1, '\n')) {
1756  if (tags & eHTTP_NcbiMsg) {
1757  /* parse NCBI message */
1758  static const char kNcbiMsgTag[] = "\n" HTTP_NCBI_MESSAGE;
1759  if (strncasecmp(s, kNcbiMsgTag, sizeof(kNcbiMsgTag) - 1) == 0) {
1760  char* msg = s + sizeof(kNcbiMsgTag) - 1, *e;
1761  while (*msg && isspace((unsigned char)(*msg)))
1762  ++msg;
1763  if (!(e = strchr(msg, '\r')) && !(e = strchr(msg, '\n')))
1764  break;
1765  if (e > msg) do {
1766  if (!isspace((unsigned char) e[-1]))
1767  break;
1768  } while (--e > msg);
1769  n = (size_t)(e - msg);
1770  if (n) {
1771  char c = msg[n];
1772  msg[n] = '\0';
1773  if (s_MessageHook) {
1774  if (s_MessageIssued <= 0) {
1775  s_MessageIssued = 1;
1776  s_MessageHook(msg);
1777  }
1778  } else {
1779  s_MessageIssued = -1;
1781  ("[NCBI-MESSAGE] %s", msg));
1782  }
1783  msg[n] = c;
1784  }
1785  tags &= (THTTP_Tags)(~eHTTP_NcbiMsg);
1786  continue;
1787  }
1788  }
1789  if (tags & eHTTP_NcbiSid) {
1790  /* parse NCBI SID */
1791  static const char kNcbiSidTag[] = "\n" HTTP_NCBI_SID;
1792  if (strncasecmp(s, kNcbiSidTag, sizeof(kNcbiSidTag) - 1) == 0) {
1793  char* sid = s + sizeof(kNcbiSidTag) - 1, *e;
1794  while (*sid && isspace((unsigned char)(*sid)))
1795  ++sid;
1796  if (!(e = strchr(sid, '\r')) && !(e = strchr(sid, '\n')))
1797  break;
1798  if (e > sid) do {
1799  if (!isspace((unsigned char) e[-1]))
1800  break;
1801  } while (--e > sid);
1802  n = (size_t)(e - sid);
1803  if (n) {
1804  char c = sid[n];
1805  sid[n] = '\0';
1807  sid[n] = c;
1808  }
1809  tags &= (THTTP_Tags)(~eHTTP_NcbiSid);
1810  continue;
1811  }
1812  }
1813  if (tags & eHTTP_Location) {
1814  /* parse "Location" pointer */
1815  static const char kLocationTag[] = "\nLocation:";
1816  assert(http_code);
1817  if (strncasecmp(s, kLocationTag, sizeof(kLocationTag) - 1) == 0) {
1818  char* loc = s + sizeof(kLocationTag) - 1, *e;
1819  while (*loc && isspace((unsigned char)(*loc)))
1820  ++loc;
1821  if (!(e = strchr(loc, '\r')) && !(e = strchr(loc, '\n')))
1822  break;
1823  if (e > loc) do {
1824  if (!isspace((unsigned char) e[-1]))
1825  break;
1826  } while (--e > loc);
1827  n = (size_t)(e - loc);
1828  if (n) {
1829  memmove(hdr, loc, n);
1830  hdr[n] = '\0';
1831  retry->data = hdr;
1832  }
1833  tags &= (THTTP_Tags)(~eHTTP_Location);
1834  continue;
1835  }
1836  }
1837  if (tags & eHTTP_Connection) {
1838  /* parse "Connection" tag */
1839  static const char kConnectionTag[] = "\nConnection:";
1840  if (strncasecmp(s, kConnectionTag, sizeof(kConnectionTag)-1) == 0){
1841  char* con = s + sizeof(kConnectionTag) - 1, *e;
1842  while (*con && isspace((unsigned char)(*con)))
1843  ++con;
1844  if (!(e = strchr(con, '\r')) && !(e = strchr(con, '\n')))
1845  break;
1846  if (e > con) do {
1847  if (!isspace((unsigned char) e[-1]))
1848  break;
1849  } while (--e > con);
1850  while ((n = (size_t)(e - con)) > 0) {
1851  const char* c = (const char*) memchr(con, ',', n);
1852  size_t m;
1853  if (c > con) {
1854  do {
1855  if (!isspace((unsigned char) c[-1]))
1856  break;
1857  } while (--c > con);
1858  m = (size_t)(c - con);
1859  } else
1860  m = n;
1861  if (m == 5 && strncasecmp(con, "close", 5) == 0) {
1862  uuu->keepalive = 0;
1863  break;
1864  }
1865  if (m == 10 && strncasecmp(con, "keep-alive", 10) == 0) {
1866  uuu->keepalive = 1;
1867  break;
1868  }
1869  if (m == n)
1870  break;
1871  con += m + 1;
1872  while (con < e && isspace((unsigned char)(*con)))
1873  ++con;
1874  }
1875  tags &= (THTTP_Tags)(~eHTTP_Connection);
1876  continue;
1877  }
1878  }
1879  if (tags & eHTTP_Authenticate) {
1880  /* parse "Authenticate"/"Proxy-Authenticate" tags */
1881  static const char kAuthenticateTag[] = "-Authenticate:";
1882  n = retry->mode == eRetry_Authenticate ? 4 : 6;
1883  assert(http_code);
1884  if (((retry->mode == eRetry_Authenticate
1885  && strncasecmp(s, "\nWWW", n) == 0) ||
1886  (retry->mode == eRetry_ProxyAuthenticate
1887  && strncasecmp(s, "\nProxy", n) == 0)) &&
1888  strncasecmp(s + n,
1889  kAuthenticateTag, sizeof(kAuthenticateTag)-1)==0) {
1890  char* txt = s + n + sizeof(kAuthenticateTag) - 1, *e;
1891  while (*txt && isspace((unsigned char)(*txt)))
1892  ++txt;
1893  if (!(e = strchr(txt, '\r')) && !(e = strchr(txt, '\n')))
1894  break;
1895  if (e > txt) do {
1896  if (!isspace((unsigned char) e[-1]))
1897  break;
1898  } while (--e > txt);
1899  n = (size_t)(e - txt);
1900  if (n && x_IsValidChallenge(txt, n)) {
1901  memmove(hdr, txt, n);
1902  hdr[n] = '\0';
1903  retry->data = hdr;
1904  }
1905  tags &= (THTTP_Tags)(~eHTTP_Authenticate);
1906  continue;
1907  }
1908  }
1909  if (tags & eHTTP_ContentLength) {
1910  /* parse "Content-Length" for non-HEAD/CONNECT */
1911  static const char kContentLenTag[] = "\nContent-Length:";
1912  if (strncasecmp(s, kContentLenTag, sizeof(kContentLenTag)-1) == 0){
1913  if (!uuu->chunked
1914  && uuu->net_info->req_method != eReqMethod_Head
1915  && uuu->net_info->req_method != eReqMethod_Connect) {
1916  const char* len = s + sizeof(kContentLenTag) - 1, *e;
1917  int tmp;
1918  while (*len && isspace((unsigned char)(*len)))
1919  ++len;
1920  if (!(e = strchr(len, '\r')) && !(e = strchr(len, '\n')))
1921  break;
1922  if (e > len) do {
1923  if (!isspace((unsigned char) e[-1]))
1924  break;
1925  } while (--e > len);
1926  if (e == len
1927  || sscanf(len, "%" NCBI_BIGCOUNT_FORMAT_SPEC "%n",
1928  &uuu->expected, &tmp) < 1
1929  || len + tmp != e) {
1930  uuu->expected = (TNCBI_BigCount)(-1L)/*no checks*/;
1931  }
1932  }
1933  tags &= (THTTP_Tags)(~eHTTP_ContentLength);
1934  continue;
1935  }
1936  }
1937  if (tags & eHTTP_TransferEncoding) {
1938  static const char kTransferEncodingTag[] = "\nTransfer-Encoding:";
1939  if (strncasecmp(s, kTransferEncodingTag,
1940  sizeof(kTransferEncodingTag)-1) == 0) {
1941  const char* te = s + sizeof(kTransferEncodingTag) - 1, *e;
1942  while (*te && isspace((unsigned char)(*te)))
1943  ++te;
1944  if (!(e = strchr(te, '\r')) && !(e = strchr(te, '\n')))
1945  break;
1946  if (e > te) do {
1947  if (!isspace((unsigned char) e[-1]))
1948  break;
1949  } while(--e > te);
1950  n = (size_t)(e - te);
1951  if (n == 7 && strncasecmp(&e[-7], "chunked", 7) == 0
1952  && (isspace((unsigned char) e[-8])
1953  || e[-8] == ':' || e[-8] == ',')) {
1954  uuu->chunked = 1;
1955  }
1956  uuu->expected = 0;
1957  tags &= (THTTP_Tags)
1958  (~(eHTTP_ContentLength | eHTTP_TransferEncoding));
1959  if (!uuu->net_info->http_version) {
1960  if (!url)
1961  url = ConnNetInfo_URL(uuu->net_info);
1963  ("[HTTP%s%s] Chunked transfer encoding"
1964  " with HTTP/1.0",
1965  url ? "; " : "",
1966  url ? url : ""));
1967  }
1968  continue;
1969  }
1970  }
1971  if (!tags)
1972  break;
1973  }
1974  if (uuu->keepalive && uuu->expected == (TNCBI_BigCount)(-1L)) {
1975  if (uuu->net_info->http_version
1976  || uuu->net_info->req_method != eReqMethod_Head) {
1977  uuu->keepalive = 0;
1978  } else
1979  uuu->expected = 0;
1980  }
1981 
1982  if (uuu->flags & fHTTP_KeepHeader) {
1983  assert(retry->mode == eRetry_None);
1984  if (!BUF_AppendEx(&uuu->r_buf, hdr, 0, hdr, size)) {
1985  int error = errno;
1986  if (!url)
1987  url = ConnNetInfo_URL(uuu->net_info);
1989  ("[HTTP%s%s] Cannot save HTTP header",
1990  url ? "; " : "",
1991  url ? url : ""));
1992  free(hdr);
1993  }
1994  if (url)
1995  free(url);
1996  return eIO_Success;
1997  }
1998 
1999  if (!retry->data)
2000  free(hdr);
2001 
2002  if (!http_code || http_code == 304) {
2003  if (url)
2004  free(url);
2005  return header_parse == eHTTP_HeaderError ? eIO_Unknown : eIO_Success;
2006  }
2007  assert(header_parse != eHTTP_HeaderComplete);
2008 
2009  if (uuu->net_info->debug_printout
2010  || header_parse == eHTTP_HeaderContinue) {
2011  if (http_code > 0/*real error, w/only a very short body expected*/)
2013  do {
2014  n = 0;
2015  status = s_ReadData(uuu, &uuu->http, 0, &n, eIO_ReadPlain);
2016  uuu->received += n;
2017  } while (status == eIO_Success);
2018  if (header_parse == eHTTP_HeaderContinue && status == eIO_Closed) {
2019  if (url)
2020  free(url);
2021  return retry->mode ? status/*eIO_Closed*/ : eIO_Success;
2022  }
2023  } else
2024  status = eIO_Success/*NB: irrelevant*/;
2025 
2027  || header_parse == eHTTP_HeaderContinue) {
2028  const char* err = status != eIO_Closed ? IO_StatusStr(status) : 0;
2029  assert(status != eIO_Success);
2030  assert(!err || *err);
2031  if (!url)
2032  url = ConnNetInfo_URL(uuu->net_info);
2033  if (header_parse == eHTTP_HeaderContinue) {
2034  assert(err/*status != eIO_Closed*/);
2036  ("[HTTP%s%s] Server error message incomplete (%s)",
2037  url ? "; " : "",
2038  url ? url : "", err));
2039  } else if (!(size = BUF_Size(uuu->http))) {
2040  CORE_LOGF_X(12, err && !(uuu->flags & fHTTP_SuppressMessages)
2042  ("[HTTP%s%s] No error message received from server"
2043  "%s%s%s",
2044  url ? "; " : "",
2045  url ? url : "",
2046  err ? " (" : "",
2047  err ? err : "", &")"[!err]));
2048  } else if ((s = (char*) malloc(size)) != 0) {
2049  n = BUF_Read(uuu->http, s, size);
2050  if (n != size) {
2051  CORE_LOGF_X(13, eLOG_Error,
2052  ("[HTTP%s%s] Cannot extract server error message"
2053  " from buffer entirely (%lu/%lu)",
2054  url ? "; " : "",
2055  url ? url : "",
2056  (unsigned long) n, (unsigned long) size));
2057  }
2058  if (n) {
2059  CORE_DATAF_X(14, eLOG_Note, s, n,
2060  ("[HTTP%s%s] Server error message%s%s%s",
2061  url ? "; " : "",
2062  url ? url : "",
2063  err ? " (" : "",
2064  err ? err : "", &")"[!err]));
2065  }
2066  free(s);
2067  } else {
2068  CORE_LOGF_X(15, eLOG_Error,
2069  ("[HTTP%s%s] Cannot allocate server error message"
2070  " (%lu byte%s)",
2071  url ? "; " : "",
2072  url ? url : "",
2073  (unsigned long) size, &"s"[size == 1]));
2074  }
2075  }
2076 
2077  if (url)
2078  free(url);
2079  if (header_parse != eHTTP_HeaderContinue)
2080  BUF_Erase(uuu->http);
2081  return eIO_Unknown;
2082 }
2083 
2084 
2085 /* Prepare connector for reading. Open socket if necessary and make an initial
2086  * connect and send, re-trying if possible until success.
2087  * Return codes:
2088  * eIO_Success = success, connector is ready for reading (uuu->sock != NULL);
2089  * eIO_Timeout = maybe (check uuu->sock) connected and no data available yet;
2090  * other code = error, not connected (uuu->sock == NULL).
2091  * NB: On error, uuu->r_buf may be updated to contain new pending data!
2092  */
2094  const STimeout* timeout,
2095  EExtractMode extract)
2096 {
2097  EIO_Status status;
2098 
2099  for (;;) {
2100  EIO_Status adjust;
2101  SRetry retry;
2102 
2103  if ((status = s_ConnectAndSend(uuu, timeout, extract)) != eIO_Success)
2104  break;
2105  assert(!uuu->w_len);
2106 
2107  if (uuu->conn_state == eCS_WriteRequest) {
2108  assert(x_IsWriteThru(uuu) && uuu->chunked);
2109  BUF_Erase(uuu->w_buf);
2110  if (!BUF_Write(&uuu->w_buf, "0\r\n\r\n", 5)) {
2111  status = eIO_Unknown;
2112  break;
2113  }
2114  uuu->w_len = 5;
2116 
2117  status = s_ConnectAndSend(uuu, timeout, extract);
2118  if (status != eIO_Success)
2119  break;
2120  assert(!uuu->w_len);
2121  }
2123 
2124  if (extract == eEM_Flush)
2125  return eIO_Success;
2126 
2127  if (extract == eEM_Wait
2128  && (uuu->conn_state & eCS_DoneBody) == eCS_DoneBody) {
2129  return eIO_Closed;
2130  }
2131 
2132  /* set read timeout before leaving (s_Read() expects it set) */
2133  SOCK_SetTimeout(uuu->sock, eIO_Read, timeout);
2134 
2135  if (uuu->conn_state & eCS_ReadBody)
2136  return eIO_Success;
2137 
2138  assert(uuu->sock && uuu->conn_state == eCS_ReadHeader);
2139  if ((status = s_ReadHeader(uuu, &retry, extract)) == eIO_Success) {
2140  assert((uuu->conn_state & eCS_ReadBody) && !retry.mode);
2141  /* pending output data no longer needed */
2142  BUF_Erase(uuu->w_buf);
2143  break;
2144  }
2145 
2146  assert(status != eIO_Timeout || !retry.mode);
2147  /* if polling then bail out with eIO_Timeout */
2148  if (status == eIO_Timeout
2149  && (extract == eEM_Wait
2150  || (timeout && !(timeout->sec | timeout->usec)))) {
2151  assert(!retry.data);
2152  return eIO_Timeout;
2153  }
2154 
2155  /* HTTP header read error; disconnect and retry */
2156  assert(status != eIO_Success);
2158  adjust = s_Adjust(uuu, &retry, status, extract);
2159  if (retry.data)
2160  free((void*) retry.data);
2161  if (adjust != eIO_Success) {
2162  if (adjust != eIO_Closed)
2163  status = adjust;
2164  break;
2165  }
2166  }
2167 
2168  if (BUF_Size(uuu->http) && !BUF_Splice(&uuu->r_buf, uuu->http))
2169  BUF_Erase(uuu->http);
2170  assert(!BUF_Size(uuu->http));
2171  return status;
2172 }
2173 
2174 
2175 /* NB: Sets the EOM read state */
2176 static void x_Close(SHttpConnector* uuu)
2177 {
2178  /* since this is merely an acknowledgement, it will be "instant" */
2180  SOCK_Destroy(uuu->sock);
2181  uuu->sock = 0;
2182  uuu->conn_state = eCS_Eom;
2183 }
2184 
2185 
2186 /* Read non-header data from connection */
2188  size_t size, size_t* n_read)
2189 {
2190  EIO_Status status = eIO_Success;
2191 
2192  assert(uuu->conn_state & eCS_ReadBody);
2194 
2195  if ((uuu->conn_state & eCS_DoneBody) == eCS_DoneBody) {
2196  if (uuu->conn_state == eCS_Eom)
2197  return eIO_Closed;
2198  if (uuu->chunked) {
2199  if (uuu->conn_state != eCS_Discard)
2200  status = x_ReadChunkTail(uuu);
2201  if (uuu->conn_state == eCS_Discard) {
2202  if (uuu->keepalive)
2203  uuu->conn_state = eCS_Eom;
2204  else
2205  x_Close(uuu);
2206  } else if (status != eIO_Closed)
2207  x_Close(uuu);
2208  } else
2209  uuu->conn_state = eCS_Eom;
2210  return status ? status : eIO_Closed;
2211  }
2212  assert(uuu->sock && size && n_read && !*n_read);
2213  assert(uuu->received <= uuu->expected);
2214 
2215  if (uuu->net_info->req_method == eReqMethod_Head
2216  || uuu->http_code / 100 == 1
2217  || uuu->http_code == 204
2218  || uuu->http_code == 304) {
2219  uuu->conn_state = eCS_Discard;
2220  status = eIO_Closed;
2221  } else if (!uuu->net_info->http_version
2222  && (uuu->flags & fHCC_UrlDecodeInput)) {
2223  /* read and URL-decode */
2224  size_t n_peeked, n_decoded;
2225  TNCBI_BigCount remain = uuu->expected - uuu->received;
2226  size_t peek_size = size > remain ? (size_t)(remain + 1) : size;
2227  void* peek_buf = malloc(peek_size *= 3);
2228 
2229  /* peek the data */
2230  status= SOCK_Read(uuu->sock,peek_buf,peek_size,&n_peeked,eIO_ReadPeek);
2231  if (status == eIO_Success) {
2232  if (URL_DecodeEx(peek_buf,n_peeked,&n_decoded,buf,size,n_read,"")){
2233  /* decode, then discard successfully decoded data from input */
2234  if (n_decoded) {
2235  SOCK_Read(uuu->sock,0,n_decoded,&n_peeked,eIO_ReadPersist);
2236  assert(n_peeked == n_decoded);
2237  uuu->received += n_decoded;
2238  } else {
2239  assert(!*n_read);
2240  if (size) {
2241  /* if at EOF and remaining data cannot be decoded */
2242  status = SOCK_Status(uuu->sock, eIO_Read);
2243  if (status == eIO_Closed)
2244  status = eIO_Unknown;
2245  }
2246  }
2247  } else
2248  status = eIO_Unknown;
2249  if (status != eIO_Success) {
2250  char* url = ConnNetInfo_URL(uuu->net_info);
2251  CORE_LOGF_X(16, eLOG_Error,
2252  ("[HTTP%s%s] Cannot URL-decode data: %s",
2253  url ? "; " : "",
2254  url ? url : "", IO_StatusStr(status)));
2255  if (url)
2256  free(url);
2257  }
2258  } else
2259  assert(!n_peeked);
2260  if (peek_buf)
2261  free(peek_buf);
2262  } else {
2263  /* just read, with no URL-decoding */
2264  status = s_ReadData(uuu, buf, size, n_read, eIO_ReadPlain);
2265  uuu->received += *n_read;
2266  }
2267 
2268  if (status == eIO_Closed) {
2269  if (!uuu->keepalive)
2270  x_Close(uuu);
2271  else if (uuu->conn_state == eCS_Discard)
2272  uuu->conn_state = eCS_Eom;
2273  }
2274 
2275  if (uuu->expected != (TNCBI_BigCount)(-1L)) {
2276  const char* how = 0;
2277  if (uuu->received < uuu->expected) {
2278  if (status == eIO_Closed) {
2279  status = eIO_Unknown;
2280  how = "Premature EOM in";
2281  }
2282  } else if (uuu->expected < uuu->received) {
2283  if (!uuu->net_info->http_version
2284  && (uuu->flags & fHCC_UrlDecodeInput)) {
2285  assert(*n_read);
2286  --(*n_read);
2287  } else {
2288  TNCBI_BigCount excess = uuu->received - uuu->expected;
2289  assert(*n_read >= excess);
2290  *n_read -= (size_t) excess;
2291  }
2292  uuu->conn_state = eCS_Discard;
2293  status = eIO_Unknown;
2294  how = "Got too much";
2295  } else if (!uuu->chunked && uuu->keepalive)
2296  uuu->conn_state = eCS_DoneBody;
2297  if (how) {
2298  char* url = ConnNetInfo_URL(uuu->net_info);
2300  ("[HTTP%s%s] %s data (received "
2301  "%" NCBI_BIGCOUNT_FORMAT_SPEC " vs. "
2302  "%" NCBI_BIGCOUNT_FORMAT_SPEC " expected)",
2303  url ? "; " : "",
2304  url ? url : "", how,
2305  uuu->received,
2306  uuu->expected != (TNCBI_BigCount)(-1L) ?
2307  uuu->expected : 0));
2308  if (url)
2309  free(url);
2310  }
2311  }
2312  return status;
2313 }
2314 
2315 
2316 /* Reset/readout input data and close socket */
2318  const STimeout* timeout,
2319  EExtractMode extract)
2320 {
2321  EIO_Status status = eIO_Success;
2322 
2323  assert(!(extract & eEM_Wait)); /* here it's only either drop or read */
2324 
2325  BUF_Erase(uuu->http);
2326  if (extract == eEM_Drop)
2327  BUF_Erase(uuu->r_buf);
2328  else if (uuu->conn_state != eCS_Eom
2329  && (status = s_PreRead(uuu, timeout, extract)) == eIO_Success) {
2330  char* x_buf = 0;
2331  do {
2332  if (x_buf || (x_buf = (char*) malloc(HTTP_SOAK_READ_SIZE)) != 0){
2333  size_t x_read = 0;
2334  status = s_Read(uuu, x_buf, HTTP_SOAK_READ_SIZE, &x_read);
2335  if (x_read < HTTP_SOAK_READ_SIZE / 2) {
2336  if (!BUF_Write(&uuu->r_buf, x_buf, x_read))
2337  status = eIO_Unknown;
2338  } else {
2339  if (!BUF_AppendEx(&uuu->r_buf,
2340  x_buf, HTTP_SOAK_READ_SIZE,
2341  x_buf, x_read)) {
2342  status = eIO_Unknown;
2343  } else
2344  x_buf = 0;
2345  }
2346  } else
2347  status = eIO_Unknown;
2348  } while (status == eIO_Success);
2349  if (x_buf)
2350  free(x_buf);
2351  if (status == eIO_Closed)
2352  status = eIO_Success;
2353  }
2354 
2355  /* s_PreRead() might have dropped the connection already */
2356  if (uuu->sock && (extract == eEM_Drop || !uuu->keepalive))
2357  s_DropConnection(uuu, eCS_Eom);
2358  uuu->can_connect &= ~fCC_Once;
2359  return status;
2360 }
2361 
2362 
2364  const STimeout* timeout)
2365 {
2366  /* NOTE: the real connect will be performed on the first "READ", or
2367  * "FLUSH/CLOSE", or on "WAIT" on read -- see in "s_ConnectAndSend" */
2368  assert(!uuu->sock);
2369 
2370  /* store timeouts for later use */
2371  if (timeout) {
2372  uuu->oo_timeout = *timeout;
2373  uuu->o_timeout = &uuu->oo_timeout;
2374  uuu->ww_timeout = *timeout;
2375  uuu->w_timeout = &uuu->ww_timeout;
2376  } else {
2377  uuu->o_timeout = kInfiniteTimeout;
2378  uuu->w_timeout = kInfiniteTimeout;
2379  }
2380 
2381  /* reset the auto-reconnect/state/re-try/auth features */
2382  uuu->can_connect = (uuu->flags & fHTTP_AutoReconnect
2385  uuu->major_fault = 0;
2386  uuu->minor_fault = 0;
2387  uuu->auth_done = 0;
2388  uuu->proxy_auth_done = 0;
2389  uuu->retry = 0;
2390 }
2391 
2392 
2394 {
2395  assert(!uuu->sock);
2396  if (uuu->cleanup)
2397  uuu->cleanup(uuu->user_data);
2399  if (uuu->vhost)
2400  free((void*) uuu->vhost);
2401  BUF_Destroy(uuu->r_buf);
2402  BUF_Destroy(uuu->w_buf);
2403  BUF_Destroy(uuu->http);
2404  free(uuu);
2405 }
2406 
2407 
2408 /***********************************************************************
2409  * INTERNAL -- "s_VT_*" functions for the "virt. table" of connector methods
2410  ***********************************************************************/
2411 
2412 #ifdef __cplusplus
2413 extern "C" {
2414 #endif /* __cplusplus */
2415  static const char* s_VT_GetType (CONNECTOR connector);
2416  static char* s_VT_Descr (CONNECTOR connector);
2417  static EIO_Status s_VT_Open (CONNECTOR connector,
2418  const STimeout* timeout);
2419  static EIO_Status s_VT_Wait (CONNECTOR connector,
2420  EIO_Event event,
2421  const STimeout* timeout);
2422  static EIO_Status s_VT_Write (CONNECTOR connector,
2423  const void* buf,
2424  size_t size,
2425  size_t* n_written,
2426  const STimeout* timeout);
2427  static EIO_Status s_VT_Flush (CONNECTOR connector,
2428  const STimeout* timeout);
2429  static EIO_Status s_VT_Read (CONNECTOR connector,
2430  void* buf,
2431  size_t size,
2432  size_t* n_read,
2433  const STimeout* timeout);
2434  static EIO_Status s_VT_Status (CONNECTOR connector,
2435  EIO_Event dir);
2436  static EIO_Status s_VT_Close (CONNECTOR connector,
2437  const STimeout* timeout);
2438  static void s_Setup (CONNECTOR connector);
2439  static void s_Destroy (CONNECTOR connector);
2440 #ifdef __cplusplus
2441 } /* extern "C" */
2442 #endif /* __cplusplus */
2443 
2444 
2445 /*ARGSUSED*/
2446 static const char* s_VT_GetType
2447 (CONNECTOR connector)
2448 {
2449  return "HTTP";
2450 }
2451 
2452 
2453 static char* s_VT_Descr
2454 (CONNECTOR connector)
2455 {
2456  return ConnNetInfo_URL(((SHttpConnector*) connector->handle)->net_info);
2457 }
2458 
2459 
2461 (CONNECTOR connector,
2462  const STimeout* timeout)
2463 {
2464  s_OpenHttpConnector((SHttpConnector*) connector->handle, timeout);
2465  return eIO_Success;
2466 }
2467 
2468 
2470 (CONNECTOR connector,
2471  EIO_Event event,
2472  const STimeout* timeout)
2473 {
2474  SHttpConnector* uuu = (SHttpConnector*) connector->handle;
2475  EIO_Status status;
2476 
2477  assert(event == eIO_Read || event == eIO_Write);
2478  switch (event) {
2479  case eIO_Read:
2480  if (BUF_Size(uuu->r_buf))
2481  return eIO_Success;
2482  if (uuu->can_connect == fCC_None)
2483  return eIO_Closed;
2484  status = s_PreRead(uuu, timeout, eEM_Wait);
2485  if (BUF_Size(uuu->r_buf))
2486  return eIO_Success;
2487  if (status != eIO_Success)
2488  return status;
2489  assert(uuu->sock);
2490  status = SOCK_Status(uuu->sock, eIO_Read);
2491  if (status != eIO_Success)
2492  return status;
2493  return SOCK_Wait(uuu->sock, eIO_Read, timeout);
2494  case eIO_Write:
2495  if (uuu->can_connect == fCC_None)
2496  return eIO_Closed;
2497  if (!x_IsWriteThru(uuu)) {
2498  return uuu->sock && uuu->can_connect == fCC_Once
2499  ? eIO_Closed : eIO_Success;
2500  }
2501  if (!uuu->sock && !BUF_Size(uuu->w_buf))
2502  return eIO_Success;
2503  if (!uuu->sock || uuu->conn_state < eCS_ReadHeader) {
2504  status = s_ConnectAndSend(uuu, timeout, eEM_Flush);
2505  if (status != eIO_Success)
2506  return status;
2507  } else
2508  return uuu->can_connect == fCC_Once ? eIO_Closed : eIO_Success;
2509  assert(uuu->sock);
2510  assert(uuu->conn_state < eCS_ReadHeader && !uuu->w_len);
2511  return uuu->conn_state < eCS_FlushRequest
2512  ? SOCK_Wait(uuu->sock, eIO_Write, timeout)
2513  : eIO_Success;
2514  default:
2515  assert(0);
2516  break;
2517  }
2518  return eIO_InvalidArg;
2519 }
2520 
2521 
2523 (CONNECTOR connector,
2524  const void* buf,
2525  size_t size,
2526  size_t* n_written,
2527  const STimeout* timeout)
2528 {
2529  SHttpConnector* uuu = (SHttpConnector*) connector->handle;
2530  EIO_Status status;
2531 
2532  assert(!*n_written);
2533 
2534  /* store the write timeout */
2535  if (timeout) {
2536  uuu->ww_timeout = *timeout;
2537  uuu->w_timeout = &uuu->ww_timeout;
2538  } else
2539  uuu->w_timeout = kInfiniteTimeout;
2540 
2541  /* if trying to write after a request then close the socket first */
2542  if (uuu->conn_state > eCS_WriteRequest) {
2543  status = s_Disconnect(uuu, timeout,
2544  uuu->flags & fHTTP_DropUnread
2545  ? eEM_Drop : eEM_Read);
2546  if (status != eIO_Success)
2547  return status;
2549  }
2550  if (uuu->can_connect == fCC_None)
2551  return eIO_Closed; /* no more connects permitted */
2552  uuu->can_connect |= fCC_Once;
2553 
2554  /* check if writing is at all legitimate */
2555  if (size && (uuu->net_info->req_method == eReqMethod_Head ||
2556  uuu->net_info->req_method == eReqMethod_Get)) {
2557  char* url = ConnNetInfo_URL(uuu->net_info);
2558  CORE_LOGF_X(24, eLOG_Error,
2559  ("[HTTP%s%s] Illegal write (%lu byte%s) with %s",
2560  url ? "; " : "",
2561  url ? url : "",
2562  (unsigned long) size, &"s"[size == 1],
2564  ? "GET" : "HEAD"));
2565  if (url)
2566  free(url);
2567  return eIO_Closed;
2568  }
2569 
2570  /* write-through with HTTP/1.1 */
2571  if (x_IsWriteThru(uuu)) {
2572  if (BUF_Size(uuu->w_buf)) {
2573  status = s_ConnectAndSend(uuu, timeout, eEM_Flush);
2574  if (status != eIO_Success)
2575  return status;
2576  }
2577  assert(!uuu->sock || (uuu->conn_state == eCS_WriteRequest
2578  && !uuu->w_len));
2579  if (size) {
2580  char prefix[80];
2581  int n = sprintf(prefix, "%" NCBI_BIGCOUNT_FORMAT_SPEC_HEX "\r\n",
2582  (TNCBI_BigCount) size);
2583  BUF_Erase(uuu->w_buf);
2584  if (!BUF_Write(&uuu->w_buf, prefix, (size_t) n) ||
2585  !BUF_Write(&uuu->w_buf, buf, size) ||
2586  !BUF_Write(&uuu->w_buf, "\r\n", 2)) {
2587  BUF_Erase(uuu->w_buf);
2588  return eIO_Unknown;
2589  }
2590  *n_written = size;
2591  size += (size_t) n + 2;
2592  uuu->w_len = size;
2593  uuu->entity = 1;
2594  }
2595  return eIO_Success;
2596  }
2597 
2598  /* accumulate all output in a memory buffer */
2599  if (size && !uuu->net_info->http_version
2600  && (uuu->flags & fHCC_UrlEncodeOutput)) {
2601  /* with URL-encoding */
2602  size_t dst_size = 3 * size;
2603  void* dst = malloc(dst_size);
2604  URL_Encode(buf, size, n_written, dst, dst_size, &dst_size);
2605  if (!*n_written
2606  || !BUF_AppendEx(&uuu->w_buf, dst, 0, dst, dst_size)) {
2607  if (dst)
2608  free(dst);
2609  return eIO_Unknown;
2610  }
2611  } else {
2612  /* "as is" (without URL-encoding) */
2613  if (!BUF_Write(&uuu->w_buf, buf, size))
2614  return eIO_Unknown;
2615  *n_written = size;
2616  }
2617  return eIO_Success;
2618 }
2619 
2620 
2622 (CONNECTOR connector,
2623  const STimeout* timeout)
2624 {
2625  SHttpConnector* uuu = (SHttpConnector*) connector->handle;
2626  EIO_Status status;
2627 
2628  if (!uuu->sock && uuu->can_connect == fCC_None)
2629  return eIO_Closed;
2630 
2631  if (timeout) {
2632  uuu->ww_timeout = *timeout;
2633  uuu->w_timeout = &uuu->ww_timeout;
2634  } else
2635  uuu->w_timeout = timeout;
2636 
2637  if (!(uuu->flags & fHTTP_Flushable))
2638  return eIO_Success;
2639 
2640  if (uuu->conn_state & eCS_ReadBody)
2641  return eIO_Success;
2642  if (uuu->sock
2643  && !(x_IsWriteThru(uuu) && uuu->conn_state < eCS_ReadHeader)) {
2644  return eIO_Success;
2645  }
2646  status = x_IsWriteThru(uuu)
2647  ? s_ConnectAndSend(uuu, timeout, eEM_Flush)
2648  : s_PreRead (uuu, timeout, eEM_Flush);
2649  return BUF_Size(uuu->r_buf) ? eIO_Success : status;
2650 }
2651 
2652 
2654 (CONNECTOR connector,
2655  void* buf,
2656  size_t size,
2657  size_t* n_read,
2658  const STimeout* timeout)
2659 {
2660  SHttpConnector* uuu = (SHttpConnector*) connector->handle;
2661  EExtractMode extract = BUF_Size(uuu->r_buf) ? eEM_Flush : eEM_Read;
2662  EIO_Status status = uuu->can_connect & fCC_Once
2663  ? s_PreRead(uuu, timeout, extract) : eIO_Unknown;
2664  size_t x_read = BUF_Read(uuu->r_buf, buf, size);
2665 
2666  assert(n_read && !*n_read);
2667  if (x_read < size && extract == eEM_Read && status == eIO_Success) {
2668  status = s_Read(uuu, (char*) buf + x_read, size - x_read, n_read);
2669  *n_read += x_read;
2670  } else
2671  *n_read = x_read;
2672  return extract == eEM_Read ? status : eIO_Success;
2673 }
2674 
2675 
2677 (CONNECTOR connector,
2678  EIO_Event dir)
2679 {
2680  SHttpConnector* uuu = (SHttpConnector*) connector->handle;
2681  return uuu->sock ? SOCK_Status(uuu->sock, dir) :
2682  (uuu->can_connect == fCC_None ? eIO_Closed : eIO_Success);
2683 }
2684 
2685 
2687 (CONNECTOR connector,
2688  const STimeout* timeout)
2689 {
2690  SHttpConnector* uuu = (SHttpConnector*) connector->handle;
2691 
2692  /* Send the accumulated output data(if any) to server, then close the
2693  * socket. Regardless of the flush, clear both input and output buffers.
2694  */
2695  if ((uuu->can_connect & fCC_Once)
2696  && ((!uuu->sock && BUF_Size(uuu->w_buf))
2697  || (uuu->flags & fHTTP_Flushable))) {
2698  /* "WRITE" mode and data (or just flag) is still pending */
2699  s_PreRead(uuu, timeout, eEM_Drop);
2700  }
2701  s_Disconnect(uuu, timeout, eEM_Drop);
2702  assert(!uuu->sock);
2703 
2704  /* clear pending output data, if any */
2705  BUF_Erase(uuu->w_buf);
2706  return eIO_Success;
2707 }
2708 
2709 
2710 static void s_Setup
2711 (CONNECTOR connector)
2712 {
2713  SHttpConnector* uuu = (SHttpConnector*) connector->handle;
2714  SMetaConnector* meta = connector->meta;
2715 
2716  /* initialize virtual table */
2717  CONN_SET_METHOD(meta, get_type, s_VT_GetType, connector);
2718  CONN_SET_METHOD(meta, descr, s_VT_Descr, connector);
2719  CONN_SET_METHOD(meta, open, s_VT_Open, connector);
2720  CONN_SET_METHOD(meta, wait, s_VT_Wait, connector);
2721  CONN_SET_METHOD(meta, write, s_VT_Write, connector);
2722  CONN_SET_METHOD(meta, flush, s_VT_Flush, connector);
2723  CONN_SET_METHOD(meta, read, s_VT_Read, connector);
2724  CONN_SET_METHOD(meta, status, s_VT_Status, connector);
2725  CONN_SET_METHOD(meta, close, s_VT_Close, connector);
2727 }
2728 
2729 
2730 static void s_Destroy
2731 (CONNECTOR connector)
2732 {
2733  SHttpConnector* uuu = (SHttpConnector*) connector->handle;
2734  connector->handle = 0;
2735 
2737  free(connector);
2738 }
2739 
2740 
2741 /* NB: per the standard, the HTTP tag name is mis-spelled as "Referer" */
2742 static const char* x_FixupUserHeader(SConnNetInfo* net_info,
2743  int /*bool*/ has_ref,
2744  int* /*bool*/ has_sid)
2745 {
2746  const char* vhost = 0, *s;
2747 
2748  if ((s = net_info->http_user_header) != 0) {
2749  int/*bool*/ first = 1/*true*/;
2750  while (*s) {
2751  if (has_ref < 0/*unset*/
2752  && strncasecmp(s, &"\nReferer:"[first], 9 - !!first) == 0) {
2753  has_ref = 1/*true*/;
2754  } else if (strncasecmp(s, &"\nHost:"[first], 6 - !!first) == 0) {
2755  if (!vhost) {
2756  vhost = s + 6 - !!first;
2757  vhost = vhost + strspn(vhost, "\t ");
2758  vhost = strndup(vhost, strcspn(vhost, ":\r\n"));
2759  }
2760  } else if (strncasecmp(s, &"\nCAF"[first], 4 - !!first) == 0
2761  && (s[4 - first] == '-' || s[4 - !!first] == ':')) {
2762  char* caftag = strndup(s + !first, strcspn(s + !first, ":"));
2763  if (caftag) {
2764  size_t cafoff = (size_t)(s - net_info->http_user_header);
2765  ConnNetInfo_DeleteUserHeader(net_info, caftag);
2766  free(caftag);
2767  if (!(s = net_info->http_user_header) || !*(s += cafoff))
2768  break;
2769  first = !cafoff;
2770  continue;
2771  }
2772  } else if (!*has_sid
2773  && strncasecmp(s, &"\n" HTTP_NCBI_SID[first],
2774  sizeof(HTTP_NCBI_SID) - !!first) == 0) {
2775  *has_sid = 1/*true*/;
2776  }
2777  if (!(s = strchr(++s, '\n')))
2778  break;
2779  first = 0/*false*/;
2780  }
2781  }
2782  s = CORE_GetAppName();
2783  if (s && *s) {
2784  char buf[128];
2785  sprintf(buf, "User-Agent: %.80s", s);
2786  ConnNetInfo_ExtendUserHeader(net_info, buf);
2787  }
2788  if ((s = net_info->http_referer) != 0) {
2789  char* ref;
2790  if (has_ref <= 0 && *s) {
2791  size_t len = strlen(s);
2792  if ((ref = (char*) realloc((char*) s, 10 + len)) != 0) {
2793  memmove(ref + 9, ref, len + 1);
2794  memcpy(ref, "Referer: ", 9);
2795  ConnNetInfo_AppendUserHeader(net_info, ref);
2796  } else
2797  ref = (char*) s;
2798  } else
2799  ref = (char*) s;
2800  net_info->http_referer = 0;
2801  assert(ref);
2802  free(ref);
2803  }
2804  if (net_info->external)
2806  return vhost;
2807 }
2808 
2809 
2811 (const SConnNetInfo* net_info,
2812  const char* user_header,
2813  int/*bool*/ tunnel,
2815  void* user_data,
2816  FHTTP_Adjust adjust,
2817  SHttpConnector** http)
2818 {
2819  SConnNetInfo* xxx;
2820  SHttpConnector* uuu;
2821  int/*bool*/ ref;
2822  int/*bool*/ sid;
2823 
2824  *http = 0;
2825  xxx = (net_info
2826  ? ConnNetInfo_Clone(net_info)
2828  if (!xxx)
2829  return eIO_Unknown;
2830 
2831  if (xxx->req_method >= eReqMethod_v1) {
2832  xxx->req_method &= ~eReqMethod_v1;
2833  xxx->http_version = 1;
2834  }
2835  if (xxx->http_version && (flags & fHTTP_PushAuth))
2836  xxx->http_push_auth = 1;
2837  if (!tunnel) {
2838  if (xxx->scheme == eURL_Unspec)
2839  xxx->scheme = eURL_Http;
2840  if (xxx->req_method == eReqMethod_Connect
2841  || (xxx->scheme != eURL_Https &&
2842  xxx->scheme != eURL_Http)) {
2843  ConnNetInfo_Destroy(xxx);
2844  return eIO_InvalidArg;
2845  }
2846  ConnNetInfo_SetFrag(xxx, "");
2847  }
2848 
2849  if (user_header && *user_header
2850  && !ConnNetInfo_OverrideUserHeader(xxx, user_header)) {
2851  ConnNetInfo_Destroy(xxx);
2852  return eIO_Unknown;
2853  }
2854 
2855  if (tunnel) {
2856  if (!xxx->http_proxy_host[0] || !xxx->http_proxy_port
2857  || xxx->http_proxy_mask == fProxy_Http) {
2858  ConnNetInfo_Destroy(xxx);
2859  return eIO_InvalidArg;
2860  }
2862  xxx->http_version = 0;
2863  xxx->path[0] = '\0';
2864  if (xxx->http_referer) {
2865  free((void*) xxx->http_referer);
2866  xxx->http_referer = 0;
2867  }
2868  ConnNetInfo_DeleteUserHeader(xxx, "Referer:");
2869  ref = 0/*false*/;
2870  } else
2871  ref = -1/*unset*/;
2872 
2873  if (!(uuu = (SHttpConnector*) malloc(sizeof(SHttpConnector)))) {
2874  ConnNetInfo_Destroy(xxx);
2875  return eIO_Unknown;
2876  }
2877 
2878  if (xxx->max_try < 1 || (flags & fHTTP_NoAutoRetry))
2879  xxx->max_try = 1;
2880 
2881  /* initialize internal data structure */
2882  uuu->net_info = xxx;
2883 
2884  uuu->parse_header = 0;
2885  uuu->user_data = user_data;
2886  uuu->adjust = adjust;
2887  uuu->cleanup = 0;
2888 
2889  sid = flags & fHTTP_NoAutomagicSID ? 1 : tunnel;
2890  uuu->vhost = x_FixupUserHeader(xxx, ref, &sid);
2891  if (sid)
2893  uuu->flags = flags;
2894 
2896  uuu->error_header = eDefault;
2897  uuu->can_connect = fCC_None; /* will be properly set at open */
2898 
2899  uuu->reserved = 0;
2900 
2901  uuu->sock = 0;
2902  uuu->o_timeout = kDefaultTimeout; /* deliberately bad values here... */
2903  uuu->w_timeout = kDefaultTimeout; /* ...must be reset prior to use */
2904  uuu->http = 0;
2905  uuu->r_buf = 0;
2906  uuu->w_buf = 0;
2907  uuu->w_len = 0;
2908 
2909  if (tunnel)
2910  s_OpenHttpConnector(uuu, xxx->timeout);
2911  /* else there are some unintialized fields left -- they are inited later */
2912 
2913  *http = uuu;
2914  return eIO_Success;
2915 }
2916 
2917 
2919 (const SConnNetInfo* net_info,
2920  const char* user_header,
2922  FHTTP_ParseHeader parse_header,
2923  void* user_data,
2924  FHTTP_Adjust adjust,
2926 {
2927  SHttpConnector* uuu;
2928  CONNECTOR ccc;
2929 
2930  if (s_CreateHttpConnector(net_info, user_header, 0/*regular*/,
2931  flags, user_data, adjust, &uuu) != eIO_Success) {
2932  assert(!uuu);
2933  return 0;
2934  }
2935  assert(uuu);
2936 
2937  if (!(ccc = (SConnector*) malloc(sizeof(SConnector)))) {
2939  return 0;
2940  }
2941 
2942  /* initialize additional internal data structure */
2943  uuu->parse_header = parse_header;
2944  uuu->cleanup = cleanup;
2945 
2946  /* enable an override from outside */
2947  if (!uuu->unsafe_redir)
2948  uuu->unsafe_redir = eDefault;
2949 
2950  /* initialize connector structure */
2951  ccc->handle = uuu;
2952  ccc->next = 0;
2953  ccc->meta = 0;
2954  ccc->setup = s_Setup;
2955  ccc->destroy = s_Destroy;
2956 
2957  return ccc;
2958 }
2959 
2960 
2961 /***********************************************************************
2962  * EXTERNAL -- the connector's "constructor"
2963  ***********************************************************************/
2964 
2966 (const SConnNetInfo* net_info,
2967  const char* user_header,
2969 {
2970  return s_CreateConnector(net_info, user_header, flags, 0, 0, 0, 0);
2971 }
2972 
2973 
2975 (const SConnNetInfo* net_info,
2977  FHTTP_ParseHeader parse_header,
2978  void* user_data,
2979  FHTTP_Adjust adjust,
2981 {
2982  return s_CreateConnector(net_info, 0/*user_header*/, flags,
2983  parse_header, user_data, adjust, cleanup);
2984 }
2985 
2986 
2988 (const SConnNetInfo* net_info,
2990  const void* init_data,
2991  size_t init_size,
2992  void* user_data,
2993  FHTTP_Adjust adjust,
2994  SOCK* sock)
2995 {
2996  unsigned short http_code;
2997  EIO_Status status;
2998  SHttpConnector* uuu;
2999 
3000  if (!sock)
3001  return eIO_InvalidArg;
3002 
3003  status = s_CreateHttpConnector(net_info, 0/*user_header*/, 1/*tunnel*/,
3005  user_data, adjust, &uuu);
3006  if (status != eIO_Success) {
3007  assert(!uuu);
3008  return status;
3009  }
3010 
3011  assert(uuu && !BUF_Size(uuu->w_buf));
3012  if (!init_size || BUF_Prepend(&uuu->w_buf, init_data, init_size)) {
3013  uuu->sock = *sock;
3014  status = s_PreRead(uuu, uuu->net_info->timeout, eEM_Wait);
3015  if (status == eIO_Success) {
3016  assert(uuu->conn_state == eCS_ReadBody);
3017  assert(uuu->http_code / 100 == 2);
3018  assert(uuu->sock);
3019  *sock = uuu->sock;
3020  uuu->sock = 0;
3021  http_code = 0;
3022  assert(*sock);
3023  } else {
3024  http_code = uuu->http_code;
3025  if (uuu->sock)
3026  s_DropConnection(uuu, eCS_Eom);
3027  *sock = 0;
3028  }
3029  } else {
3030  http_code = 0;
3031  status = eIO_Unknown;
3032  }
3033 
3035  switch (http_code) {
3036  case 503:
3037  return eIO_NotSupported;
3038  case 426:
3039  case 404:
3040  return eIO_InvalidArg;
3041  case 403:
3042  return eIO_Closed;
3043  default:
3044  break;
3045  }
3046  return status;
3047 }
3048 
3049 
3051 (const SConnNetInfo* net_info,
3053  SOCK* sock)
3054 {
3055  return HTTP_CreateTunnelEx(net_info, flags, 0, 0, 0, 0, sock);
3056 }
3057 
3058 
3060 {
3061  if (hook) {
3062  if (hook != s_MessageHook)
3063  s_MessageIssued = s_MessageIssued ? -1 : -2;
3064  } else if (s_MessageIssued < -1)
3065  s_MessageIssued = 0;
3066  s_MessageHook = hook;
3067 }
static uch flags
int close(int fd)
Definition: connection.cpp:45
static void cleanup(void)
Definition: ct_dynamic.c:30
CS_CONTEXT * ctx
Definition: t0006.c:12
static DLIST_TYPE *DLIST_NAME() first(DLIST_LIST_TYPE *list)
Definition: dlist.tmpl.h:46
static void fatal(const char *msg,...)
Definition: attributes.c:18
static const char * str(char *buf, int n)
Definition: stats.c:84
static char tmp[3200]
Definition: utf8.c:42
char data[12]
Definition: iconv.c:80
@ eOff
Definition: ncbi_types.h:110
@ eDefault
Definition: ncbi_types.h:112
@ eOn
Definition: ncbi_types.h:111
void BUF_Erase(BUF buf)
Definition: ncbi_buffer.c:461
int BUF_Write(BUF *pBuf, const void *data, size_t size)
Definition: ncbi_buffer.c:224
int BUF_Prepend(BUF *pBuf, const void *data, size_t size)
Definition: ncbi_buffer.c:218
size_t BUF_Size(BUF buf)
Definition: ncbi_buffer.c:84
int BUF_AppendEx(BUF *pBuf, void *base, size_t alloc_size, void *data, size_t size)
Definition: ncbi_buffer.c:138
size_t BUF_Read(BUF buf, void *data, size_t size)
Definition: ncbi_buffer.c:414
int BUF_Splice(BUF *dst, BUF src)
Definition: ncbi_buffer.c:477
size_t BUF_Peek(BUF buf, void *data, size_t size)
Definition: ncbi_buffer.c:408
size_t BUF_PeekAtCB(BUF buf, size_t pos, size_t(*callback)(void *cbdata, const void *buf, size_t size), void *cbdata, size_t size)
void BUF_Destroy(BUF buf)
Definition: ncbi_buffer.c:500
void(* FHTTP_NcbiMessageHook)(const char *message)
EIO_Status HTTP_CreateTunnel(const SConnNetInfo *net_info, THTTP_Flags flags, SOCK *sock)
Same as HTTP_CreateTunnelEx(net_info, flags, 0, 0, 0, 0, sock)
FSetupVTable setup
init meta, may not be NULL
#define CONN_SET_METHOD(meta, method, function, connector)
CONNECTOR HTTP_CreateConnectorEx(const SConnNetInfo *net_info, THTTP_Flags flags, FHTTP_ParseHeader parse_header, void *user_data, FHTTP_Adjust adjust, FHTTP_Cleanup cleanup)
Create new CONNECTOR structure to hit the specified URL using HTTP with either POST / GET (or ANY) me...
unsigned int THTTP_Flags
Bitwise OR of EHTTP_Flag.
int(* FHTTP_Adjust)(SConnNetInfo *net_info, void *user_data, unsigned int failure_count)
#define CONN_SET_DEFAULT_TIMEOUT(meta, timeout)
void(* FHTTP_Cleanup)(void *user_data)
CONNECTOR next
linked list
CONNECTOR HTTP_CreateConnector(const SConnNetInfo *net_info, const char *user_header, THTTP_Flags flags)
Same as HTTP_CreateConnector(net_info, flags, 0, 0, 0, 0) with the passed "user_header" overriding th...
void HTTP_SetNcbiMessageHook(FHTTP_NcbiMessageHook hook)
Set a message hook procedure for messages originating from NCBI via HTTP.
FDestroy destroy
destroys handle, can be NULL
EHTTP_HeaderParse
The extended version HTTP_CreateConnectorEx() is able to track the HTTP response chain and also chang...
void * handle
data handle of the connector
SMetaConnector * meta
back link to original meta
EHTTP_HeaderParse(* FHTTP_ParseHeader)(const char *http_header, void *user_data, int server_error)
EIO_Status HTTP_CreateTunnelEx(const SConnNetInfo *net_info, THTTP_Flags flags, const void *init_data, size_t init_size, void *user_data, FHTTP_Adjust adjust, SOCK *sock)
Create a tunnel to "net_info->host:net_info->port" via an HTTP proxy server located at "net_info->htt...
const STimeout g_NcbiDefConnTimeout
DEF_CONN_TIMEOUT as STimeout.
Definition: ncbi_connutil.c:54
@ fHTTP_DropUnread
Each microsession drops unread data.
@ fHTTP_AdjustOnRedirect
Call adjust routine for redirects, too.
@ fHTTP_NoUpread
Do not use SOCK_SetReadOnWrite()
@ fHTTP_NoAutoRetry
No auto-retries allowed.
@ fHTTP_AutoReconnect
See HTTP_CreateConnectorEx()
@ fHTTP_KeepHeader
Keep HTTP header (see limitations)
@ fHTTP_NoAutomagicSID
Do not add NCBI SID automagically.
@ fHTTP_SuppressMessages
Most annoying ones reduced to traces.
@ fHTTP_UnsafeRedirects
Any redirect will be honored.
@ fHTTP_Flushable
Connector will really flush on Flush()
@ fHTTP_PushAuth
HTTP/1.1 pushes out auth if present.
@ fHTTP_WriteThru
HTTP/1.1 writes through (chunked)
@ eHTTP_HeaderSuccess
Parse succeeded, retain server status.
@ eHTTP_HeaderError
Parse failed, treat as a server error.
@ eHTTP_HeaderContinue
Parse succeeded, continue with body.
@ eHTTP_HeaderComplete
Parse succeeded, no more processing.
@ fHCC_UrlEncodeOutput
Obsolete, may not work, do not use!
@ fHCC_UrlDecodeInput
Obsolete, may not work, do not use!
@ fHCC_UrlEncodeArgs
NB: Error-prone semantics, do not use!
int SOCK_isipEx(const char *host, int fullquad)
Check whether the given string represents a valid IPv4 address.
Definition: ncbi_socket.c:8681
int SOCK_IsSecure(SOCK sock)
Check whether a socket is using SSL (Secure Socket Layer).
Definition: ncbi_socket.c:8465
EIO_Status SOCK_SetTimeout(SOCK sock, EIO_Event event, const STimeout *timeout)
Specify timeout for the connection I/O (see SOCK_[Read|Write|Close]()).
Definition: ncbi_socket.c:7195
#define SOCK_Destroy(s)
Definition: ncbi_socket.h:875
EIO_Status SOCK_Close(SOCK sock)
Close the SOCK handle, and destroy all relevant internal data.
Definition: ncbi_socket.c:6852
EIO_Status SOCK_Read(SOCK sock, void *buf, size_t size, size_t *n_read, EIO_ReadMethod how)
Read/peek up to "size" bytes from "sock" to a buffer pointed to by "buf".
Definition: ncbi_socket.c:7271
EIO_Status SOCK_Status(SOCK sock, EIO_Event direction)
Return low-level socket I/O status of *last* socket operation.
Definition: ncbi_socket.c:7460
unsigned int SOCK_gethostbyname(const char *hostname)
Same as SOCK_gethostbynameEx(,<current API data logging>)
Definition: ncbi_socket.c:8791
EIO_Status SOCK_Pushback(SOCK sock, const void *data, size_t size)
Push the specified data back to the socket's input queue (in the socket's internal read buffer).
Definition: ncbi_socket.c:7440
EIO_Status SOCK_Wait(SOCK sock, EIO_Event event, const STimeout *timeout)
Block on the socket until either the specified "event" is available or "timeout" expires (if "timeout...
Definition: ncbi_socket.c:6954
EIO_Status SOCK_Abort(SOCK sock)
If there is outstanding connection or output data pending, cancel it.
Definition: ncbi_socket.c:7558
EIO_Status SOCK_Write(SOCK sock, const void *data, size_t size, size_t *n_written, EIO_WriteMethod how)
Write "size" bytes of "data" to "sock".
Definition: ncbi_socket.c:7487
const STimeout * SOCK_GetTimeout(SOCK sock, EIO_Event event)
Get the connection's i/o timeout (or NULL, if the timeout is infinite).
Definition: ncbi_socket.c:7231
const char * SOCK_gethostbyaddr(unsigned int addr, char *name, size_t namelen)
Same as SOCK_gethostbyaddrEx(,,<current API data logging>)
Definition: ncbi_socket.c:8814
unsigned int TSOCK_Flags
bitwise "OR" of ESOCK_Flags
Definition: ncbi_socket.h:503
@ fSOCK_LogDefault
Definition: ncbi_socket.h:491
@ fSOCK_KeepAlive
keep socket alive (if supported by OS)
Definition: ncbi_socket.h:492
@ fSOCK_Secure
subsumes CloseOnExec regardless of Keep
Definition: ncbi_socket.h:497
@ fSOCK_ReadOnWrite
Definition: ncbi_socket.h:500
@ fSOCK_LogOn
Definition: ncbi_socket.h:490
ELOG_Level
Log severity level.
Definition: ncbi_core.h:292
TReqMethod req_method
unsigned EBURLScheme
unsigned external
char http_proxy_host[255+1]
EBFWMode firewall
int ConnNetInfo_ExtendUserHeader(SConnNetInfo *net_info, const char *header)
EIO_Status URL_ConnectEx(const char *host, unsigned short port, const char *path, const char *args, TReqMethod req_method, size_t content_length, const STimeout *o_timeout, const STimeout *rw_timeout, const char *user_header, SURLExtra *extra, TSOCK_Flags flags, SOCK *sock)
char http_proxy_user[63+1]
unsigned short http_proxy_port
#define kInfiniteTimeout
Definition: ncbi_types.h:82
#define NCBI_BIGCOUNT_FORMAT_SPEC_HEX
Definition: ncbi_types.h:166
int ConnNetInfo_OverrideUserHeader(SConnNetInfo *net_info, const char *header)
int ConnNetInfo_PreOverrideUserHeader(SConnNetInfo *net_info, const char *header)
const STimeout * timeout
unsigned short port
int ConnNetInfo_ParseURL(SConnNetInfo *net_info, const char *url)
unsigned http_push_auth
int ConnNetInfo_SetArgs(SConnNetInfo *net_info, const char *args)
int ConnNetInfo_AppendUserHeader(SConnNetInfo *net_info, const char *header)
SConnNetInfo * ConnNetInfo_Clone(const SConnNetInfo *net_info)
LOG CORE_GetLOG(void)
Get the log handle that is to be used by the core internals (CORE LOG).
Definition: ncbi_util.c:138
char user[63+1]
EIO_Status
I/O status.
Definition: ncbi_core.h:132
unsigned int usec
microseconds (modulo 1,000,000)
Definition: ncbi_types.h:78
char http_proxy_pass[63+1]
const char * ConnNetInfo_GetArgs(const SConnNetInfo *net_info)
EReqMethod
const char svc[1]
char * ConnNetInfo_URL(const SConnNetInfo *net_info)
const char * IO_StatusStr(EIO_Status status)
Get the text form of an enum status value.
Definition: ncbi_core.c:56
const char * CORE_GetAppName(void)
Obtain current application name (toolkit dependent).
Definition: ncbi_util.c:720
EIO_ReadMethod
I/O read method.
Definition: ncbi_core.h:88
int ConnNetInfo_SetUserHeader(SConnNetInfo *net_info, const char *header)
void ConnNetInfo_DeleteUserHeader(SConnNetInfo *net_info, const char *header)
void ConnNetInfo_Log(const SConnNetInfo *net_info, ELOG_Level sev, LOG log)
EBURLScheme scheme
const char * http_user_header
EBProxyType http_proxy_mask
unsigned short max_try
char * CORE_GetNcbiRequestID(ENcbiRequestID reqid)
Obtain current NCBI request ID (if known, per thread).
Definition: ncbi_util.c:747
#define ConnNetInfo_PostOverrideUserHeader
unsigned int sec
seconds
Definition: ncbi_types.h:77
NCBI_CRED credentials
char pass[63+1]
EIO_Status SOCK_StripToPattern(SOCK sock, const void *pattern, size_t pattern_size, BUF *discard, size_t *n_discarded)
unsigned http_version
EIO_Event
I/O event (or direction).
Definition: ncbi_core.h:118
void URL_Encode(const void *src_buf, size_t src_size, size_t *src_read, void *dst_buf, size_t dst_size, size_t *dst_written)
#define NCBI_BIGCOUNT_FORMAT_SPEC
Definition: ncbi_types.h:165
char host[255+1]
int ConnNetInfo_Boolean(const char *str)
#define kDefaultTimeout
Definition: ncbi_types.h:81
char path[4095+1]
EBDebugPrintout debug_printout
int URL_DecodeEx(const void *src_buf, size_t src_size, size_t *src_read, void *dst_buf, size_t dst_size, size_t *dst_written, const char *allow_symbols)
const char * http_referer
void ConnNetInfo_Destroy(SConnNetInfo *net_info)
int ConnNetInfo_SetFrag(SConnNetInfo *net_info, const char *frag)
uint64_t TNCBI_BigCount
Big unsigned integer for file size and position.
Definition: ncbi_types.h:164
@ eLOG_Critical
Definition: ncbi_core.h:298
@ eLOG_Error
Definition: ncbi_core.h:297
@ eLOG_Note
Definition: ncbi_core.h:294
@ eLOG_Warning
Definition: ncbi_core.h:296
@ eLOG_Trace
Definition: ncbi_core.h:293
@ eURL_Unspec
@ eURL_Http
@ eURL_Https
@ eIO_Timeout
timeout expired before any I/O succeeded
Definition: ncbi_core.h:134
@ eIO_NotSupported
operation is not supported or is not available
Definition: ncbi_core.h:138
@ eIO_Success
everything is fine, no error occurred
Definition: ncbi_core.h:133
@ eIO_Unknown
unknown I/O error (likely fatal but can retry)
Definition: ncbi_core.h:139
@ eIO_InvalidArg
bad argument / parameter value(s) supplied
Definition: ncbi_core.h:137
@ eReqMethod_Any
@ eReqMethod_v1
@ eReqMethod_Put
@ eReqMethod_Delete
@ eReqMethod_Get
@ eReqMethod_Connect
@ eReqMethod_Head
@ eReqMethod_Post
@ fProxy_Http
$http_proxy used
@ fProxy_Https
$https_proxy used
@ eDebugPrintout_Some
@ eDebugPrintout_Data
@ eIO_ReadPlain
read readily available data only, wait if none
Definition: ncbi_core.h:90
@ eIO_ReadPeek
do eIO_ReadPlain but leave data in input queue
Definition: ncbi_core.h:89
@ eIO_ReadPersist
read exactly as much as requested, w/waits
Definition: ncbi_core.h:91
@ eIO_WritePlain
write as much as possible, report back how much
Definition: ncbi_core.h:101
@ eIO_Write
write
Definition: ncbi_core.h:121
@ eIO_Close
also serves as an error indicator in SOCK_Poll
Definition: ncbi_core.h:123
@ eIO_Read
read
Definition: ncbi_core.h:120
@ eNcbiRequestID_SID
NCBI Session ID.
Definition: ncbi_util.h:444
@ eNcbiRequestID_HitID
NCBI Hit ID.
Definition: ncbi_util.h:443
char * buf
if(yy_accept[yy_current_state])
yy_size_t n
int len
static void text(MDB_val *v)
Definition: mdb_dump.c:62
const struct ncbi::grid::netcache::search::fields::SIZE size
char * strncpy0(char *s1, const char *s2, size_t n)
Copy not more than "n" characters from string "s2" into "s1", and return the result,...
#define strdup
Definition: ncbi_ansi_ext.h:70
#define strncasecmp
#define strcasecmp
#define strndup
Definition: ncbi_ansi_ext.h:89
#define verify(expr)
Definition: ncbi_assert.h:51
#define NCBI_EXTERNAL
Definition: ncbi_comm.h:57
#define HTTP_NCBI_SID
Definition: ncbi_comm.h:43
#define HTTP_NCBI_PHID
Definition: ncbi_comm.h:44
#define HTTP_NCBI_MESSAGE
Definition: ncbi_comm.h:42
SConnNetInfo * ConnNetInfo_CloneInternal(const SConnNetInfo *info)
const char * ConnNetInfo_GetValueInternal(const char *service, const char *param, char *value, size_t value_size, const char *def_value)
SConnNetInfo * ConnNetInfo_CreateInternal(const char *service)
#define CONN_PORT_HTTP
Definition: ncbi_connutil.h:95
#define CONN_HOST_LEN
#define CONN_PORT_HTTPS
Definition: ncbi_connutil.h:96
#define CONN_USER_LEN
Definition: ncbi_connutil.h:99
#define CONN_PASS_LEN
static int x_IsValidChallenge(const char *text, size_t len)
static EIO_Status x_ReadChunkTail(SHttpConnector *uuu)
static EHTTP_Redirect x_Redirect(SHttpConnector *uuu, const SRetry *retry)
static int x_SamePort(unsigned short port1, EBURLScheme scheme1, unsigned short port2, EBURLScheme scheme2)
static const char kHttpHostTag[]
EHTTP_Redirect
@ eHTTP_RedirectInvalid
@ eHTTP_RedirectUnsafe
@ eHTTP_RedirectOK
@ eHTTP_RedirectError
@ eHTTP_RedirectTunnel
static int x_UnsafeRedirectOK(SHttpConnector *uuu)
static EIO_Status s_Connect(SHttpConnector *uuu, const STimeout *timeout, EExtractMode extract)
static int x_Pushback(SOCK sock, BUF buf)
static const char * x_SetHttpHostTag(SConnNetInfo *net_info)
@ eCS_ReadBody
@ eCS_Eom
@ eCS_ReadHeader
@ eCS_WriteRequest
@ eCS_FlushRequest
@ eCS_DoneBody
@ eCS_Discard
@ eCS_NotInitiated
static EIO_Status s_Adjust(SHttpConnector *uuu, const SRetry *retry, EIO_Status status, EExtractMode extract)
static void s_DestroyHttpConnector(SHttpConnector *uuu)
unsigned EBSwitch
static char * s_VT_Descr(CONNECTOR connector)
unsigned EBConnState
static EIO_Status s_Read(SHttpConnector *uuu, void *buf, size_t size, size_t *n_read)
static EIO_Status s_VT_Write(CONNECTOR connector, const void *buf, size_t size, size_t *n_written, const STimeout *timeout)
@ eRetry_Redirect303
@ eRetry_Authenticate
@ eRetry_Unused
@ eRetry_ProxyAuthenticate
@ eRetry_None
@ eRetry_Redirect
static EIO_Status s_VT_Read(CONNECTOR connector, void *buf, size_t size, size_t *n_read, const STimeout *timeout)
static char * x_HostPort(size_t reserve, const char *host, unsigned short xport)
EExtractMode
@ eEM_Wait
@ eEM_Flush
@ eEM_Read
@ eEM_Drop
static const char * s_VT_GetType(CONNECTOR connector)
static EIO_Status s_ReadData(SHttpConnector *uuu, void *buf, size_t size, size_t *n_read, EIO_ReadMethod how)
static int x_IsValidParam(const char *param, size_t paramlen)
static EIO_Status s_ReadHeader(SHttpConnector *uuu, SRetry *retry, EExtractMode extract)
static int x_SameProxyHost(const char *host1, const char *host2)
static int x_RedirectOK(EBURLScheme scheme_to, const char *host_to, unsigned short port_to, EBURLScheme scheme_from, const char *host_from, unsigned short port_from)
static int x_SameScheme(EBURLScheme scheme1, EBURLScheme scheme2)
static EIO_Status s_VT_Close(CONNECTOR connector, const STimeout *timeout)
@ eHTTP_AuthMissing
@ eHTTP_AuthIllegal
@ eHTTP_AuthUnsafe
@ eHTTP_AuthNotSupp
@ eHTTP_AuthOK
@ eHTTP_AuthError
@ eHTTP_AuthConnect
@ eHTTP_AuthDone
static int s_TunnelAdjust(SConnNetInfo *net_info, void *data, unsigned int arg)
static int s_CallAdjust(SHttpConnector *uuu, unsigned int arg)
static EIO_Status s_VT_Flush(CONNECTOR connector, const STimeout *timeout)
static int x_ErrorHeaderOnly(SHttpConnector *uuu)
static EIO_Status s_VT_Open(CONNECTOR connector, const STimeout *timeout)
static EIO_Status s_ConnectAndSend(SHttpConnector *uuu, const STimeout *timeout, EExtractMode extract)
#define HTTP_SOAK_READ_SIZE
unsigned EBCanConnect
static CONNECTOR s_CreateConnector(const SConnNetInfo *net_info, const char *user_header, THTTP_Flags flags, FHTTP_ParseHeader parse_header, void *user_data, FHTTP_Adjust adjust, FHTTP_Cleanup cleanup)
static void x_SetRequestIDs(SConnNetInfo *net_info)
static size_t x_PushbackBuf(void *data, const void *buf, size_t size)
static int x_IsWriteThru(const SHttpConnector *uuu)
static void s_OpenHttpConnector(SHttpConnector *uuu, const STimeout *timeout)
static EIO_Status s_Disconnect(SHttpConnector *uuu, const STimeout *timeout, EExtractMode extract)
static EIO_Status x_ReadChunkHead(SHttpConnector *uuu, int first)
static const char * x_FixupUserHeader(SConnNetInfo *net_info, int has_ref, int *has_sid)
static int s_MessageIssued
static FHTTP_NcbiMessageHook s_MessageHook
@ fCC_Once
@ fCC_None
@ fCC_Unlimited
static size_t x_WriteBuf(void *data, const void *buf, size_t size)
static void s_Setup(CONNECTOR connector)
static void s_DropConnection(SHttpConnector *uuu, enum EConnState state)
static EIO_Status s_CreateHttpConnector(const SConnNetInfo *net_info, const char *user_header, int tunnel, THTTP_Flags flags, void *user_data, FHTTP_Adjust adjust, SHttpConnector **http)
static unsigned short x_PortForScheme(unsigned port, EBURLScheme scheme)
static int x_SameOrDefPort(unsigned short port1, EBURLScheme scheme1, unsigned short port2, EBURLScheme scheme2)
static EIO_Status s_VT_Status(CONNECTOR connector, EIO_Event dir)
static EIO_Status s_VT_Wait(CONNECTOR connector, EIO_Event event, const STimeout *timeout)
static EIO_Status s_PreRead(SHttpConnector *uuu, const STimeout *timeout, EExtractMode extract)
static void x_Close(SHttpConnector *uuu)
static EHTTP_Auth x_Authenticate(SHttpConnector *uuu, ERetry auth, int retry)
static void s_Destroy(CONNECTOR connector)
static const STimeout kZeroTimeout
#define CORE_DATAF_X(subcode, level, data, size, fmt_args)
Definition: ncbi_priv.h:198
#define CORE_LOGF_X(subcode, level, fmt_args)
Definition: ncbi_priv.h:150
#define CORE_LOGF_ERRNO_X(subcode, level, error, fmt_args)
Definition: ncbi_priv.h:166
#define CORE_UNLOCK
Definition: ncbi_priv.h:273
#define CORE_LOCK_READ
Definition: ncbi_priv.h:271
const char * tag
#define NCBI_CXX_TOOLKIT
Definition: ncbiconf_msvc.h:16
int isspace(Uchar c)
Definition: ncbictype.hpp:69
#define memmove(a, b, c)
char * strerror(int n)
Definition: pcre2grep.c:1177
static SLJIT_INLINE sljit_ins msg(sljit_gpr r, sljit_s32 d, sljit_gpr x, sljit_gpr b)
#define BASE64_Decode
Definition: ncbi_base64.h:42
#define BASE64_Encode
Definition: ncbi_base64.h:41
#define assert(x)
Definition: srv_diag.hpp:58
Connector specification.
TNCBI_BigCount expected
unsigned short http_code
EBConnState conn_state
const STimeout * o_timeout
unsigned char minor_fault
EBCanConnect can_connect
unsigned short major_fault
TNCBI_BigCount received
FHTTP_Cleanup cleanup
SConnNetInfo * net_info
FHTTP_ParseHeader parse_header
const STimeout * w_timeout
Standard set of connector methods to handle a connection (corresponding connectors are also in here),...
const char * data
Timeout structure.
Definition: ncbi_types.h:76
Extra URL_ConnectEx() parameters.
void free(voidpf ptr)
voidp malloc(uInt size)
Modified on Fri Sep 20 14:58:13 2024 by modify_doxy.py rev. 669887