NCBI C Toolkit Cross Reference

C/connect/ncbi_http_connector.c


  1 /* $Id: ncbi_http_connector.c,v 6.103 2009/10/30 14:44:36 kazimird Exp $
  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 <connect/ncbi_http_connector.h>
 40 #include <ctype.h>
 41 #include <errno.h>
 42 #include <stdlib.h>
 43 
 44 #define NCBI_USE_ERRCODE_X   Connect_HTTP
 45 
 46 
 47 /***********************************************************************
 48  *  INTERNAL -- Auxiliary types and static functions
 49  ***********************************************************************/
 50 
 51 /* Whether the connector is allowed to connect
 52  */
 53 typedef enum {
 54     eCC_None,
 55     eCC_Once,
 56     eCC_Unlimited
 57 } ECanConnect;
 58 
 59 typedef unsigned       EBCanConnect;
 60 typedef unsigned short TBHCC_Flags;
 61 
 62 typedef enum {
 63     eRM_Regular    = 0,
 64     eRM_DropUnread = 1,
 65     eRM_WaitCalled = 2
 66 } EReadMode;
 67 
 68 
 69 /* All internal data necessary to perform the (re)connect and I/O
 70  *
 71  * The following states are defined:
 72  *  "sock"  | "read_header" | State description
 73  * ---------+---------------+--------------------------------------------------
 74  *   NULL   |  <whatever>   | User "WRITE" mode: accumulate data in buffer
 75  * non-NULL |   non-zero    | HTTP header is being read
 76  * non-NULL |     zero      | HTTP body is being read (user "READ" mode)
 77  * ---------+---------------+--------------------------------------------------
 78  */
 79 typedef struct {
 80     SConnNetInfo*        net_info;        /* network configuration parameters*/
 81     FHttpParseHTTPHeader parse_http_hdr;  /* callback to parse HTTP reply hdr*/
 82     FHttpAdjustNetInfo   adjust_net_info; /* for on-the-fly net_info adjust  */
 83     FHttpAdjustCleanup   adjust_cleanup;  /* supplemental user data...       */
 84     void*                adjust_data;     /* ...and cleanup routine          */
 85 
 86     TBHCC_Flags          flags;           /* as passed to constructor        */
 87     unsigned             error_header:1;  /* only err.HTTP header on SOME dbg*/
 88     EBCanConnect         can_connect:2;   /* whether more conns permitted    */
 89     unsigned             read_header:1;   /* whether reading header          */
 90     unsigned             shut_down:1;     /* whether shut down for write     */
 91     unsigned             auth_sent:1;     /* authenticate sent               */
 92     unsigned             reserved:7;      /* MBZ                             */
 93     unsigned             minor_fault:3;   /* incr each min failure since maj */
 94     unsigned short       major_fault;     /* incr each maj failure since open*/
 95     unsigned short       code;            /* last response code              */
 96 
 97     SOCK                 sock;         /* socket;  NULL if not in "READ" mode*/
 98     const STimeout*      o_timeout;    /* NULL(infinite), dflt or ptr to next*/
 99     STimeout             oo_timeout;   /* storage for (finite) open timeout  */
100     const STimeout*      w_timeout;    /* NULL(infinite), dflt or ptr to next*/
101     STimeout             ww_timeout;   /* storage for a (finite) write tmo   */
102 
103     BUF                  http;         /* storage for HTTP reply header      */
104     BUF                  r_buf;        /* storage to accumulate input data   */
105     BUF                  w_buf;        /* storage to accumulate output data  */
106     size_t               w_len;        /* pending message body size          */
107 
108     size_t               expected;     /* expected to receive until EOF      */
109     size_t               received;     /* actually received so far           */
110 } SHttpConnector;
111 
112 
113 /* NCBI messaging support */
114 static int                   s_MessageIssued = 0;
115 static FHTTP_NcbiMessageHook s_MessageHook   = 0;
116 
117 
118 typedef enum {
119     eRetry_None = 0,
120     eRetry_Redirect,
121     eRetry_Authenticate,
122     eRetry_ProxyAuthenticate
123 } ERetry;
124 
125 
126 typedef struct {
127     ERetry      mode;
128     const char* data;
129 } SRetry;
130 
131 
132 /* Try to fix connection parameters (called for an unconnected connector) */
133 static EIO_Status s_Adjust(SHttpConnector* uuu,
134                            const SRetry*   retry,
135                            EReadMode       read_mode)
136 {
137     EIO_Status status;
138 
139     assert(!uuu->sock  &&  uuu->can_connect != eCC_None);
140 
141     if (retry  &&  retry->mode  &&  ++uuu->minor_fault < 3) {
142         int fail;
143         /* adjust info before yet another connection attempt */
144         switch (retry->mode) {
145         case eRetry_Redirect:
146             if (retry->data  &&  retry->data[0] != '?') {
147                 if (uuu->net_info->req_method == eReqMethod_Get
148                     ||  (uuu->flags & fHCC_InsecureRedirect)
149                     ||  !uuu->w_len) {
150                     int secure = uuu->net_info->scheme == eURL_Https ? 1 : 0;
151                     *uuu->net_info->args = '\0'/*arguments not inherited*/;
152                     fail = !ConnNetInfo_ParseURL(uuu->net_info, retry->data);
153                     if (!fail  &&  secure
154                         &&  uuu->net_info->scheme != eURL_Https
155                         &&  !(uuu->flags & fHCC_InsecureRedirect)) {
156                         fail = -1;
157                     }
158                 } else
159                     fail = -1;
160             } else
161                 fail = 1;
162             if (fail) {
163                 CORE_LOGF_X(2, eLOG_Error,
164                             ("[HTTP]  %s redirect to %s%s%s",
165                              fail < 0 ? "Prohibited" : "Cannot",
166                              retry->data ? "\""        : "<",
167                              retry->data ? retry->data : "NULL",
168                              retry->data ? "\""        : ">"));
169                 status = eIO_Closed;
170             } else
171                 status = eIO_Success;
172             break;
173         case eRetry_Authenticate:
174         case eRetry_ProxyAuthenticate:
175             fail = uuu->auth_sent ? 1 : -1;
176             CORE_LOGF_X(3, eLOG_Error,
177                         ("[HTTP]  %s %s %c%s%c",
178                          retry->mode == eRetry_Authenticate
179                          ? "Authorization" : "Proxy authorization",
180                          fail < 0 ? "not yet implemented" : "failed",
181                          "(<"[!retry->data],
182                          retry->data ? retry->data : "NULL",
183                          ")>"[!retry->data]));
184             status = fail < 0 ? eIO_NotSupported : eIO_Closed;
185             break;
186         default:
187             status = eIO_Unknown;
188             assert(0);
189             break;
190         }
191         if (status != eIO_Success) {
192             uuu->can_connect = eCC_None;
193             return status;
194         }
195     } else {
196         const char* msg;
197 
198         uuu->minor_fault = 0;
199         /* we're here because something is going wrong */
200         if (++uuu->major_fault >= uuu->net_info->max_try) {
201             msg = read_mode != eRM_DropUnread  &&  uuu->major_fault > 1
202                 ? "[HTTP]  Too many failed attempts (%d), giving up" : "";
203         } else if (!uuu->adjust_net_info
204                    ||  uuu->adjust_net_info(uuu->net_info,
205                                             uuu->adjust_data,
206                                             uuu->major_fault) == 0) {
207             msg = read_mode != eRM_DropUnread  &&  uuu->major_fault > 1
208                 ? "[HTTP]  Retry attempts (%d) exhausted, giving up" : "";
209         } else
210             msg = 0;
211         if (msg) {
212             if (*msg) {
213                 CORE_LOGF_X(1, eLOG_Error,
214                             (msg, uuu->major_fault));
215             }
216             uuu->can_connect = eCC_None;
217             return eIO_Closed;
218         }
219     }
220 
221     ConnNetInfo_AdjustForHttpProxy(uuu->net_info);
222     return eIO_Success;
223 }
224 
225 
226 /* Unconditionally drop the connection; timeout may specify time allowance */
227 static void s_DropConnection(SHttpConnector* uuu, const STimeout* timeout)
228 {
229     assert(uuu->sock);
230     BUF_Erase(uuu->http);
231     if (uuu->read_header)
232         SOCK_Abort(uuu->sock);
233     else
234         SOCK_SetTimeout(uuu->sock, eIO_Close, timeout);
235     SOCK_Close(uuu->sock);
236     uuu->sock = 0;
237 }
238 
239 
240 /* Connect to the HTTP server, specified by uuu->net_info's "port:host".
241  * Return eIO_Success only if socket connection has succeeded and uuu->sock
242  * is non-zero. If unsuccessful, try to adjust uuu->net_info by s_Adjust(),
243  * and then re-try the connection attempt.
244  */
245 static EIO_Status s_Connect(SHttpConnector* uuu,
246                             EReadMode       read_mode)
247 {
248     EIO_Status status;
249 
250     assert(!uuu->sock);
251     if (uuu->can_connect == eCC_None) {
252         if (read_mode == eRM_Regular)
253             CORE_LOG_X(5, eLOG_Error, "[HTTP]  Connector no longer usable");
254         return eIO_Closed;
255     }
256 
257     /* the re-try loop... */
258     for (;;) {
259         int/*bool*/ reset_user_header = 0;
260         char*       http_user_header = 0;
261         TSOCK_Flags flags;
262 
263         uuu->w_len = BUF_Size(uuu->w_buf);
264         if (uuu->net_info->http_user_header)
265             http_user_header = strdup(uuu->net_info->http_user_header);
266         if (!uuu->net_info->http_user_header == !http_user_header) {
267             ConnNetInfo_ExtendUserHeader
268                 (uuu->net_info, "User-Agent: NCBIHttpConnector"
269 #ifdef NCBI_CXX_TOOLKIT
270                  " (C++ Toolkit)"
271 #else
272                  " (C Toolkit)"
273 #endif
274                  "\r\n");
275             reset_user_header = 1;
276         }
277         if (uuu->net_info->debug_printout)
278             ConnNetInfo_Log(uuu->net_info, CORE_GetLOG());
279         flags = (uuu->net_info->debug_printout == eDebugPrintout_Data
280                  ? fSOCK_LogOn : fSOCK_LogDefault);
281         if (uuu->net_info->scheme == eURL_Https)
282             flags |= fSOCK_Secure;
283         if (!(uuu->flags & fHCC_NoUpread))
284             flags |= fSOCK_ReadOnWrite;
285         /* connect & send HTTP header */
286         if ((status = URL_ConnectEx
287              (uuu->net_info->host,       uuu->net_info->port,
288               uuu->net_info->path,       uuu->net_info->args,
289               uuu->net_info->req_method, uuu->w_len,
290               uuu->o_timeout,            uuu->w_timeout,
291               uuu->net_info->http_user_header,
292               (int/*bool*/)(uuu->flags & fHCC_UrlEncodeArgs),
293               flags, &uuu->sock)) != eIO_Success) {
294             uuu->sock = 0;
295         }
296         if (reset_user_header) {
297             ConnNetInfo_SetUserHeader(uuu->net_info, 0);
298             uuu->net_info->http_user_header = http_user_header;
299         }
300         if (uuu->sock)
301             break/*success*/;
302 
303         /* connection failed, no socket was created */
304         if (s_Adjust(uuu, 0, read_mode) != eIO_Success)
305             break;
306     }
307 
308     return status;
309 }
310 
311 
312 /* Connect to the server specified by uuu->net_info, then compose and form
313  * relevant HTTP header, and flush the accumulated output data(uuu->w_buf)
314  * after the HTTP header. If connection/write unsuccessful, retry to reconnect
315  * and send the data again until permitted by s_Adjust().
316  */
317 static EIO_Status s_ConnectAndSend(SHttpConnector* uuu,
318                                    EReadMode       read_mode)
319 {
320     EIO_Status status;
321 
322     for (;;) {
323         size_t body_size;
324         char   buf[4096];
325 
326         if (!uuu->sock) {
327             if ((status = s_Connect(uuu, read_mode)) != eIO_Success)
328                 break;
329             assert(uuu->sock);
330             uuu->read_header = 1/*true*/;
331             uuu->shut_down = 0/*false*/;
332             uuu->expected = 0;
333             uuu->received = 0;
334             uuu->code = 0;
335         } else
336             status = eIO_Success;
337 
338         if (uuu->w_len) {
339             size_t off = BUF_Size(uuu->w_buf) - uuu->w_len;
340 
341             SOCK_SetTimeout(uuu->sock, eIO_Write, uuu->w_timeout);
342             do {
343                 size_t n_written;
344                 size_t n_write = BUF_PeekAt(uuu->w_buf, off, buf, sizeof(buf));
345                 status = SOCK_Write(uuu->sock, buf, n_write,
346                                     &n_written, eIO_WritePlain);
347                 if (status != eIO_Success)
348                     break;
349                 uuu->w_len -= n_written;
350                 off        += n_written;
351             } while (uuu->w_len);
352         } else if (!uuu->shut_down)
353             status = SOCK_Write(uuu->sock, 0, 0, 0, eIO_WritePlain);
354 
355         if (status == eIO_Success) {
356             assert(uuu->w_len == 0);
357             if (!uuu->shut_down) {
358                 /* 10/07/03: While this call here is perfectly legal, it could
359                  * cause connection severed by a buggy CISCO load-balancer. */
360                 /* 10/28/03: CISCO's beta patch for their LB shows that the
361                  * problem has been fixed; no more 2'30" drops in connections
362                  * that shut down for write.  We still leave this commented
363                  * out to allow unpatched clients to work seamlessly... */ 
364                 /*SOCK_Shutdown(uuu->sock, eIO_Write);*/
365                 uuu->shut_down = 1;
366             }
367             break;
368         }
369 
370         if (status == eIO_Timeout
371             &&  (read_mode == eRM_WaitCalled
372                  ||  (uuu->w_timeout
373                       &&  !(uuu->w_timeout->sec | uuu->w_timeout->usec)))) {
374             break;
375         }
376         if ((body_size = BUF_Size(uuu->w_buf))) {
377             sprintf(buf, "writing request body at offset %lu",
378                     (unsigned long)(body_size - uuu->w_len));
379         } else
380             strcpy(buf, "flushing request header");
381         CORE_LOGF_X(6, eLOG_Error,
382                     ("[HTTP]  Error %s (%s)", buf, IO_StatusStr(status)));
383 
384         /* write failed; close and try to use another server */
385         s_DropConnection(uuu, 0/*no wait*/);
386         if ((status = s_Adjust(uuu, 0, read_mode)) != eIO_Success)
387             break/*closed*/;
388     }
389 
390     return status;
391 }
392 
393 
394 static int/*bool*/ s_IsValidParam(const char* param, size_t parlen)
395 {
396     const char* e = (const char*) memchr(param, '=', parlen);
397     size_t toklen;
398     if (!e  ||  e == param)
399         return 0/*false*/;
400     if ((toklen = (size_t)(++e - param)) >= parlen)
401         return 0/*false*/;
402     assert(!isspace((unsigned char)(*param)));
403     if (strcspn(param, " \t") < toklen)
404         return 0/*false*/;
405     if (*e == '\''  ||  *e == '"') {
406         /* a quoted string */
407         toklen = parlen - toklen;
408         if (!(e = (const char*) memchr(e + 1, *e, --toklen)))
409             return 0/*false*/;
410         e++/*skip the quote*/;
411     } else
412         e += strcspn(e, " \t");
413     if (e != param + parlen  &&  e + strspn(e, " \t") != param + parlen)
414         return 0/*false*/;
415     return 1/*true*/;
416 }
417 
418 
419 static int/*bool*/ s_IsValidAuth(const char* challenge, size_t len)
420 {
421     /* Challenge must contain a scheme name token and a non-empty param
422      * list (comma-separated pairs token={token|quoted_string}), with at
423      * least one parameter being named "realm=".
424      */
425     size_t word = strcspn(challenge, " \t");
426     int retval = 0/*false*/;
427     if (word < len) {
428         /* 1st word is always the scheme name */
429         const char* param = challenge + word;
430         for (param += strspn(param, " \t");  param < challenge + len;
431              param += strspn(param, ", \t")) {
432             size_t parlen = (size_t)(challenge + len - param);
433             const char* c = (const char*) memchr(param, ',', parlen);
434             if (c)
435                 parlen = (size_t)(c - param);
436             if (!s_IsValidParam(param, parlen))
437                 return 0/*false*/;
438             if (parlen > 6  &&  strncasecmp(param, "realm=", 6) == 0)
439                 retval = 1/*true, but keep scanning*/;
440             param += c ? ++parlen : parlen;
441         }
442     }
443     return retval;
444 }
445 
446 
447 /* Parse HTTP header */
448 static EIO_Status s_ReadHeader(SHttpConnector* uuu,
449                                SRetry*         retry,
450                                EReadMode       read_mode)
451 {
452     int        server_error = 0;
453     int        http_status;
454     EIO_Status status;
455     char*      header;
456     size_t     size;
457 
458     assert(uuu->sock  &&  uuu->read_header);
459     retry->mode = eRetry_None;
460     retry->data = 0;
461 
462     /* line by line HTTP header input */
463     for (;;) {
464         /* do we have full header yet? */
465         size = BUF_Size(uuu->http);
466         if (!(header = (char*) malloc(size + 1))) {
467             CORE_LOGF_X(7, eLOG_Error,
468                         ("[HTTP]  Cannot allocate header, %lu bytes",
469                          (unsigned long) size));
470             return eIO_Unknown;
471         }
472         verify(BUF_Peek(uuu->http, header, size) == size);
473         header[size] = '\0';
474         if (size >= 4  &&  strcmp(&header[size - 4], "\r\n\r\n") == 0)
475             break/*full header captured*/;
476         free(header);
477 
478         status = SOCK_StripToPattern(uuu->sock, "\r\n", 2, &uuu->http, 0);
479         if (status != eIO_Success) {
480             ELOG_Level level;
481             if (status == eIO_Timeout) {
482                 const STimeout* tmo = SOCK_GetTimeout(uuu->sock, eIO_Read);
483                 if (!tmo)
484                     level = eLOG_Error;
485                 else if (read_mode == eRM_WaitCalled)
486                     return status;
487                 else if (tmo->sec | tmo->usec)
488                     level = eLOG_Warning;
489                 else
490                     level = eLOG_Trace;
491             } else
492                 level = eLOG_Error;
493             CORE_LOGF_X(8, level, ("[HTTP]  Error reading header (%s)",
494                                    IO_StatusStr(status)));
495             return status;
496         }
497     }
498     /* the entire header has been read */
499     uuu->read_header = 0/*false*/;
500     status = eIO_Success;
501     BUF_Erase(uuu->http);
502 
503     /* HTTP status must come on the first line of the response */
504     if (sscanf(header, "HTTP/%*d.%*d %d ", &http_status) != 1 || !http_status)
505         http_status = -1;
506     if (http_status < 200  ||  299 < http_status) {
507         server_error = http_status;
508         if (http_status == 301  ||  http_status == 302  ||  http_status == 307)
509             retry->mode = eRetry_Redirect;
510         else if (http_status == 401)
511             retry->mode = eRetry_Authenticate;
512         else if (http_status == 407)
513             retry->mode = eRetry_ProxyAuthenticate;
514         else if (http_status < 0  ||  http_status == 403 || http_status == 404)
515             uuu->net_info->max_try = 0;
516     }
517     uuu->code = http_status < 0 ? -1 : http_status;
518 
519     if ((server_error  ||  !uuu->error_header)
520         &&  uuu->net_info->debug_printout == eDebugPrintout_Some) {
521         /* HTTP header gets printed as part of data logging when
522            uuu->net_info->debug_printout == eDebugPrintout_Data. */
523         const char* header_header;
524         if (!server_error)
525             header_header = "HTTP header";
526         else if (uuu->flags & fHCC_KeepHeader)
527             header_header = "HTTP header (error)";
528         else if (uuu->code == 301  ||  uuu->code == 302  ||  uuu->code == 307)
529             header_header = "HTTP header (moved)";
530         else if (!uuu->net_info->max_try)
531             header_header = "HTTP header (unrecoverable error)";
532         else
533             header_header = "HTTP header (server error, can retry)";
534         CORE_DATA_X(19, header, size, header_header);
535     }
536 
537     {{
538         /* parsing "NCBI-Message" tag */
539         static const char kNcbiMessageTag[] = "\n" HTTP_NCBI_MESSAGE " ";
540         const char* s;
541         for (s = strchr(header, '\n');  s  &&  *s;  s = strchr(s + 1, '\n')) {
542             if (strncasecmp(s, kNcbiMessageTag, sizeof(kNcbiMessageTag)-1)==0){
543                 const char* message = s + sizeof(kNcbiMessageTag) - 1;
544                 while (*message  &&  isspace((unsigned char)(*message)))
545                     message++;
546                 if (!(s = strchr(message, '\r')))
547                     s = strchr(message, '\n');
548                 assert(s);
549                 do {
550                     if (!isspace((unsigned char) s[-1]))
551                         break;
552                 } while (--s > message);
553                 if (message != s) {
554                     if (s_MessageHook) {
555                         if (s_MessageIssued <= 0) {
556                             s_MessageIssued  = 1;
557                             s_MessageHook(message);
558                         }
559                     } else {
560                         s_MessageIssued = -1;
561                         CORE_LOGF_X(10, eLOG_Critical,
562                                     ("[NCBI-MESSAGE]  %.*s",
563                                      (int)(s - message), message));
564                     }
565                 }
566                 break;
567             }
568         }
569     }}
570 
571     if (uuu->flags & fHCC_KeepHeader) {
572         if (!BUF_Write(&uuu->r_buf, header, size)) {
573             CORE_LOG_X(11, eLOG_Error, "[HTTP]  Cannot keep HTTP header");
574             status = eIO_Unknown;
575         }
576         free(header);
577         return status;
578     }
579 
580     if (uuu->parse_http_hdr
581         &&  !(*uuu->parse_http_hdr)(header, uuu->adjust_data, server_error)) {
582         server_error = 1/*fake, but still boolean true*/;
583     }
584 
585     if (retry->mode == eRetry_Redirect) {
586         /* parsing "Location" pointer */
587         static const char kLocationTag[] = "\nLocation: ";
588         char* s;
589         for (s = strchr(header, '\n');  s  &&  *s;  s = strchr(s + 1, '\n')) {
590             if (strncasecmp(s, kLocationTag, sizeof(kLocationTag) - 1) == 0) {
591                 char* location = s + sizeof(kLocationTag) - 1;
592                 while (*location  &&  isspace((unsigned char)(*location)))
593                     location++;
594                 if (!(s = strchr(location, '\r')))
595                     s = strchr(location, '\n');
596                 if (!s)
597                     break;
598                 do {
599                     if (!isspace((unsigned char) s[-1]))
600                         break;
601                 } while (--s > location);
602                 if (s != location) {
603                     size_t len = (size_t)(s - location);
604                     memmove(header, location, len);
605                     header[len] = '\0';
606                     retry->data = header;
607                 }
608                 break;
609             }
610         }
611     } else if (retry->mode != eRetry_None) {
612         /* parsing "Authenticate" tags */
613         static const char kAuthenticateTag[] = "-Authenticate: ";
614         char* s;
615         for (s = strchr(header, '\n');  s  &&  *s;  s = strchr(s + 1, '\n')) {
616             if (strncasecmp(s + (retry->mode == eRetry_Authenticate ? 4 : 6),
617                             kAuthenticateTag, sizeof(kAuthenticateTag)-1)==0){
618                 if ((retry->mode == eRetry_Authenticate
619                      &&  strncasecmp(s, "\nWWW",   4) == 0)  ||
620                     (retry->mode == eRetry_ProxyAuthenticate
621                      &&  strncasecmp(s, "\nProxy", 6) == 0)) {
622                     char* challenge = s + sizeof(kAuthenticateTag) - 1, *e;
623                     challenge += retry->mode == eRetry_Authenticate ? 4 : 6;
624                     while (*challenge && isspace((unsigned char)(*challenge)))
625                         challenge++;
626                     if (!(e = strchr(challenge, '\r')))
627                         e = strchr(challenge, '\n');
628                     else
629                         s = e;
630                     if (!e)
631                         break;
632                     do {
633                         if (!isspace((unsigned char) e[-1]))
634                             break;
635                     } while (--e > challenge);
636                     if (e != challenge) {
637                         size_t len = (size_t)(e - challenge);
638                         if (s_IsValidAuth(challenge, len)) {
639                             memmove(header, challenge, len);
640                             header[len] = '\0';
641                             retry->data = header;
642                             break;
643                         }
644                     }
645                 }
646             }
647         }
648     } else if (!server_error) {
649         static const char kContentLengthTag[] = "\nContent-Length: ";
650         const char* s;
651         for (s = strchr(header, '\n');  s  &&  *s;  s = strchr(s + 1, '\n')) {
652             if (!strncasecmp(s,kContentLengthTag,sizeof(kContentLengthTag)-1)){
653                 const char* expected = s + sizeof(kContentLengthTag) - 1;
654                 while (*expected  &&  isspace((unsigned char)(*expected)))
655                     expected++;
656                 if (!(s = strchr(expected, '\r')))
657                     s = strchr(expected, '\n');
658                 assert(s);
659                 do {
660                     if (!isspace((unsigned char) s[-1]))
661                         break;
662                 } while (--s > expected);
663                 if (s != expected) {
664                     char* e;
665                     errno = 0;
666                     uuu->expected = (size_t) strtol(expected, &e, 10);
667                     if (errno  ||  e != s)
668                         uuu->expected = 0;
669                     else if (!uuu->expected)
670                         uuu->expected = (size_t)(-1L);
671                 }
672                 break;
673             }
674         }
675     }
676     if (!retry->data)
677         free(header);
678 
679     /* skip & printout the content, if server error was flagged */
680     if (server_error && uuu->net_info->debug_printout == eDebugPrintout_Some) {
681         BUF   buf = 0;
682         char* body;
683 
684         SOCK_SetTimeout(uuu->sock, eIO_Read, 0);
685         /* read until EOF */
686         SOCK_StripToPattern(uuu->sock, 0, 0, &buf, 0);
687         if (!(size = BUF_Size(buf))) {
688             CORE_LOG_X(12, eLOG_Trace,
689                        "[HTTP]  No HTTP body received with this error");
690         } else if ((body = (char*) malloc(size)) != 0) {
691             size_t n = BUF_Read(buf, body, size);
692             if (n != size) {
693                 CORE_LOGF_X(13, eLOG_Error,
694                             ("[HTTP]  Cannot read server error "
695                              "body from buffer (%lu out of %lu)",
696                              (unsigned long) n, (unsigned long) size));
697             }
698             CORE_DATA_X(20, body, n, "Server error body");
699             free(body);
700         } else {
701             CORE_LOGF_X(14, eLOG_Error,
702                         ("[HTTP]  Cannot allocate server error "
703                          "body, %lu bytes", (unsigned long) size));
704         }
705         BUF_Destroy(buf);
706     }
707 
708     return server_error ? eIO_Unknown : status;
709 }
710 
711 
712 /* Prepare connector for reading. Open socket if necessary and
713  * make initial connect and send, re-trying if possible until success.
714  * Return codes:
715  *   eIO_Success = success, connector is ready for reading (uuu->sock != NULL);
716  *   eIO_Timeout = maybe (check uuu->sock) connected and no data yet available;
717  *   other code  = error, not connected (uuu->sock == NULL).
718  */
719 static EIO_Status s_PreRead(SHttpConnector* uuu,
720                             const STimeout* timeout,
721                             EReadMode       read_mode)
722 {
723     EIO_Status status;
724 
725     for (;;) {
726         EIO_Status adjust_status;
727         SRetry     retry;
728 
729         status = s_ConnectAndSend(uuu, read_mode);
730         if (!uuu->sock) {
731             assert(status != eIO_Success);
732             break;
733         }
734         if (status != eIO_Success) {
735             if (status != eIO_Timeout  ||
736                 status == SOCK_Status(uuu->sock, eIO_Read)/*pending*/)
737                 break;
738         }
739 
740         /* set timeout */
741         verify(SOCK_SetTimeout(uuu->sock, eIO_Read, timeout) == eIO_Success);
742 
743         if (!uuu->read_header)
744             break;
745 
746         if ((status = s_ReadHeader(uuu, &retry, read_mode)) == eIO_Success) {
747             /* pending output data no longer needed */
748             assert(!uuu->read_header  &&  !retry.mode);
749             BUF_Erase(uuu->w_buf);
750             break;
751         }
752         assert(status != eIO_Timeout  ||  !retry.mode);
753         /* if polling then bail out with eIO_Timeout */
754         if (status == eIO_Timeout
755             &&  (read_mode == eRM_WaitCalled
756                  ||  (timeout  &&  !(timeout->sec | timeout->usec)))) {
757             assert(!retry.data);
758             break;
759         }
760 
761         /* HTTP header read error; disconnect and try to use another server */
762         s_DropConnection(uuu, 0/*no wait*/);
763         adjust_status = s_Adjust(uuu, retry.mode ? &retry : 0, read_mode);
764         if (retry.data)
765             free((void*) retry.data);
766         if (adjust_status != eIO_Success) {
767             if (adjust_status != eIO_Closed)
768                 status = adjust_status;
769             break;
770         }
771     }
772 
773     return status;
774 }
775 
776 
777 /* Read non-header data from connection */
778 static EIO_Status s_Read(SHttpConnector* uuu, void* buf,
779                          size_t size, size_t* n_read)
780 {
781     EIO_Status status;
782 
783     assert(uuu->sock);
784     if (uuu->flags & fHCC_UrlDecodeInput) {
785         /* read and URL-decode */
786         size_t     n_peeked, n_decoded;
787         size_t     peek_size = 3 * size;
788         void*      peek_buf  = malloc(peek_size);
789 
790         /* peek the data */
791         status= SOCK_Read(uuu->sock,peek_buf,peek_size,&n_peeked,eIO_ReadPeek);
792         if (status != eIO_Success) {
793             assert(!n_peeked);
794             *n_read = 0;
795         } else {
796             if (URL_Decode(peek_buf,n_peeked,&n_decoded,buf,size,n_read)) {
797                 /* decode, then discard successfully decoded data from input */
798                 if (n_decoded) {
799                     SOCK_Read(uuu->sock,0,n_decoded,&n_peeked,eIO_ReadPersist);
800                     assert(n_peeked == n_decoded);
801                     uuu->received += n_decoded;
802                     status = eIO_Success;
803                 } else if (SOCK_Status(uuu->sock, eIO_Read) == eIO_Closed) {
804                     /* we are at EOF, and remaining data cannot be decoded */
805                     status = eIO_Unknown;
806                 }
807             } else
808                 status = eIO_Unknown;
809 
810             if (status != eIO_Success)
811                 CORE_LOG_X(16, eLOG_Error, "[HTTP]  Cannot URL-decode data");
812         }
813         free(peek_buf);
814     } else {
815         /* just read, with no URL-decoding */
816         status = SOCK_Read(uuu->sock, buf, size, n_read, eIO_ReadPlain);
817         uuu->received += *n_read;
818     }
819 
820     if (uuu->expected) {
821         if (uuu->received > uuu->expected)
822             return eIO_Unknown/*received too much*/;
823         if (uuu->expected != (size_t)(-1L)) {
824             if (status == eIO_Closed  &&  uuu->expected > uuu->received)
825                 return eIO_Unknown/*received too little*/;
826         } else if (uuu->received)
827             return eIO_Unknown/*received too much*/;
828     }
829     return status;
830 }
831 
832 
833 /* Reset/readout input data and close socket */
834 static EIO_Status s_Disconnect(SHttpConnector* uuu,
835                                const STimeout* timeout,
836                                EReadMode       read_mode)
837 {
838     EIO_Status status = eIO_Success;
839 
840     if (read_mode == eRM_DropUnread)
841         BUF_Erase(uuu->r_buf);
842     else if ((status = s_PreRead(uuu, timeout, read_mode)) == eIO_Success) {
843         do {
844             char   buf[4096];
845             size_t x_read;
846             status = s_Read(uuu, buf, sizeof(buf), &x_read);
847             if (!BUF_Write(&uuu->r_buf, buf, x_read))
848                 status = eIO_Unknown;
849         } while (status == eIO_Success);
850         if (status == eIO_Closed)
851             status = eIO_Success;
852     }
853 
854     if (uuu->sock) /* s_PreRead() might have dropped the connection already */
855         s_DropConnection(uuu, timeout);
856     if (uuu->can_connect == eCC_Once)
857         uuu->can_connect = eCC_None;
858 
859     return status;
860 }
861 
862 
863 /* Send the accumulated output data(if any) to server, then close socket.
864  * Regardless of the flush, clear both input and output buffer.
865  * This function is only called to either re-open or close the connector.
866  */
867 static void s_FlushAndDisconnect(SHttpConnector* uuu,
868                                  int/*bool*/     close,
869                                  const STimeout* timeout)
870 {
871     /* store timeouts for later use */
872     if (timeout) {
873         uuu->oo_timeout = *timeout;
874         uuu->o_timeout  = &uuu->oo_timeout;
875         uuu->ww_timeout = *timeout;
876         uuu->w_timeout  = &uuu->ww_timeout;
877     } else {
878         uuu->o_timeout  = timeout;
879         uuu->w_timeout  = timeout;
880     }
881 
882     if (close  &&  uuu->can_connect != eCC_None  &&  !uuu->sock
883         &&  ((uuu->flags & fHCC_SureFlush)  ||  BUF_Size(uuu->w_buf))) {
884         /* "WRITE" mode and data (or just flag) pending */
885         s_PreRead(uuu, timeout, eRM_DropUnread);
886     }
887     s_Disconnect(uuu, timeout, eRM_DropUnread);
888     assert(!uuu->sock);
889 
890     /* clear pending output data, if any */
891     BUF_Erase(uuu->w_buf);
892 }
893 
894 
895 /***********************************************************************
896  *  INTERNAL -- "s_VT_*" functions for the "virt. table" of connector methods
897  ***********************************************************************/
898 
899 #ifdef __cplusplus
900 extern "C" {
901 #endif /* __cplusplus */
902     static const char* s_VT_GetType (CONNECTOR       connector);
903     static char*       s_VT_Descr   (CONNECTOR       connector);
904     static EIO_Status  s_VT_Open    (CONNECTOR       connector,
905                                      const STimeout* timeout);
906     static EIO_Status  s_VT_Wait    (CONNECTOR       connector,
907                                      EIO_Event       event,
908                                      const STimeout* timeout);
909     static EIO_Status  s_VT_Write   (CONNECTOR       connector,
910                                      const void*     buf,
911                                      size_t          size,
912                                      size_t*         n_written,
913                                      const STimeout* timeout);
914     static EIO_Status  s_VT_Flush   (CONNECTOR       connector,
915                                      const STimeout* timeout);
916     static EIO_Status  s_VT_Read    (CONNECTOR       connector,
917                                      void*           buf,
918                                      size_t          size,
919                                      size_t*         n_read,
920                                      const STimeout* timeout);
921     static EIO_Status  s_VT_Status  (CONNECTOR       connector,
922                                      EIO_Event       dir);
923     static EIO_Status  s_VT_Close   (CONNECTOR       connector,
924                                      const STimeout* timeout);
925 
926     static void        s_Setup      (SMetaConnector *meta,
927                                      CONNECTOR connector);
928     static void        s_Destroy    (CONNECTOR connector);
929 #  ifdef IMPLEMENTED__CONN_WaitAsync
930     static EIO_Status s_VT_WaitAsync(CONNECTOR     connector,
931                                      FConnectorAsyncHandler  func,
932                                      SConnectorAsyncHandler* data);
933 #  endif
934 #ifdef __cplusplus
935 } /* extern "C" */
936 #endif /* __cplusplus */
937 
938 
939 /*ARGSUSED*/
940 static const char* s_VT_GetType
941 (CONNECTOR connector)
942 {
943     return "HTTP";
944 }
945 
946 
947 static char* s_VT_Descr
948 (CONNECTOR connector)
949 {
950     return ConnNetInfo_URL(((SHttpConnector*) connector->handle)->net_info);
951 }
952 
953 
954 static EIO_Status s_VT_Open
955 (CONNECTOR       connector,
956  const STimeout* timeout)
957 {
958     SHttpConnector* uuu = (SHttpConnector*) connector->handle;
959 
960     /* NOTE: the real connect will be performed on the first "READ", or
961      * "CLOSE", or on "WAIT" on read -- see in "s_ConnectAndSend()";
962      * we just close underlying socket and prepare to open it later */
963     s_FlushAndDisconnect(uuu, 0/*open*/, timeout);
964 
965     /* reset the auto-reconnect feature */
966     uuu->can_connect = uuu->flags & fHCC_AutoReconnect
967         ? eCC_Unlimited : eCC_Once;
968     uuu->major_fault = 0;
969     uuu->minor_fault = 0;
970     uuu->auth_sent = 0;
971 
972     return eIO_Success;
973 }
974 
975 
976 static EIO_Status s_VT_Wait
977 (CONNECTOR       connector,
978  EIO_Event       event,
979  const STimeout* timeout)
980 {
981     SHttpConnector* uuu = (SHttpConnector*) connector->handle;
982     EIO_Status status;
983 
984     switch (event) {
985     case eIO_Read:
986         if (BUF_Size(uuu->r_buf))
987             return eIO_Success;
988         if (uuu->can_connect == eCC_None)
989             return eIO_Closed;
990         if ((status = s_PreRead(uuu, timeout, eRM_WaitCalled)) != eIO_Success)
991             return status;
992         assert(uuu->sock);
993         return SOCK_Wait(uuu->sock, eIO_Read, timeout);
994     case eIO_Write:
995         /* Return 'Closed' if no more writes are allowed (and now - reading) */
996         return uuu->can_connect == eCC_None
997             ||  (uuu->sock  &&  uuu->can_connect == eCC_Once)
998             ? eIO_Closed : eIO_Success;
999     default:
1000         break;
1001     }
1002     assert(0);
1003     return eIO_InvalidArg;
1004 }
1005 
1006 
1007 static EIO_Status s_VT_Write
1008 (CONNECTOR       connector,
1009  const void*     buf,
1010  size_t          size,
1011  size_t*         n_written,
1012  const STimeout* timeout)
1013 {
1014     SHttpConnector* uuu = (SHttpConnector*) connector->handle;
1015 
1016     /* if trying to "WRITE" after "READ" then close the socket,
1017      * and so switch to "WRITE" mode */
1018     if (uuu->sock) {
1019         EIO_Status status = s_Disconnect(uuu, timeout,
1020                                          uuu->flags & fHCC_DropUnread
1021                                          ? eRM_DropUnread : eRM_Regular);
1022         if (status != eIO_Success)
1023             return status;
1024     }
1025     if (uuu->can_connect == eCC_None)
1026         return eIO_Closed; /* no more connects permitted */
1027 
1028     /* accumulate all output in the memory buffer */
1029     if (uuu->flags & fHCC_UrlEncodeOutput) {
1030         /* with URL-encoding */
1031         size_t dst_size = 3 * size;
1032         void* dst = malloc(dst_size);
1033         size_t dst_written;
1034         URL_Encode(buf, size, n_written, dst, dst_size, &dst_written);
1035         assert(*n_written == size);
1036         if (!BUF_Write(&uuu->w_buf, dst, dst_written)) {
1037             free(dst);
1038             return eIO_Unknown;
1039         }
1040         free(dst);
1041     } else {
1042         /* "as is" (without URL-encoding) */
1043         if (!BUF_Write(&uuu->w_buf, buf, size))
1044             return eIO_Unknown;
1045         *n_written = size;
1046     }
1047 
1048     /* store the write timeout */
1049     if (timeout) {
1050         uuu->ww_timeout = *timeout;
1051         uuu->w_timeout  = &uuu->ww_timeout;
1052     } else
1053         uuu->w_timeout  = timeout;
1054 
1055     return eIO_Success;
1056 }
1057 
1058 
1059 static EIO_Status s_VT_Flush
1060 (CONNECTOR       connector,
1061  const STimeout* timeout)
1062 {
1063     SHttpConnector* uuu = (SHttpConnector*) connector->handle;
1064 
1065     assert(connector->meta);
1066     if (!(uuu->flags & fHCC_Flushable)  ||  uuu->sock) {
1067         /* The real flush will be performed on the first "READ" (or "CLOSE"),
1068          * or on "WAIT". Here, we just store the write timeout, that's all...
1069          * ADDENDUM: fHCC_Flushable connectors are able to actually flush data.
1070          */
1071         if (timeout) {
1072             uuu->ww_timeout = *timeout;
1073             uuu->w_timeout  = &uuu->ww_timeout;
1074         } else
1075             uuu->w_timeout  = timeout;
1076         
1077         return eIO_Success;
1078     }
1079     if (!connector->meta->wait)
1080         return eIO_NotSupported;
1081 
1082     return connector->meta->wait(connector->meta->c_wait, eIO_Read, timeout);
1083 }
1084 
1085 
1086 static EIO_Status s_VT_Read
1087 (CONNECTOR       connector,
1088  void*           buf,
1089  size_t          size,
1090  size_t*         n_read,
1091  const STimeout* timeout)
1092 {
1093     SHttpConnector* uuu = (SHttpConnector*) connector->handle;
1094     EIO_Status status = s_PreRead(uuu, timeout, eRM_Regular);
1095     size_t x_read = BUF_Read(uuu->r_buf, buf, size);
1096 
1097     *n_read = x_read;
1098     if (x_read < size) {
1099         if (status != eIO_Success)
1100             return status;
1101         status = s_Read(uuu, (char*) buf + x_read, size - x_read, n_read);
1102         *n_read += x_read;
1103     } else
1104         status = eIO_Success;
1105 
1106     return *n_read ? eIO_Success : status;
1107 }
1108 
1109 
1110 static EIO_Status s_VT_Status
1111 (CONNECTOR connector,
1112  EIO_Event dir)
1113 {
1114     SHttpConnector* uuu = (SHttpConnector*) connector->handle;
1115     return uuu->sock ? SOCK_Status(uuu->sock, dir) :
1116         (uuu->can_connect == eCC_None ? eIO_Closed : eIO_Success);
1117 }
1118 
1119 
1120 static EIO_Status s_VT_Close
1121 (CONNECTOR       connector,
1122  const STimeout* timeout)
1123 {
1124     s_FlushAndDisconnect((SHttpConnector*) connector->handle,
1125                          1/*close*/, timeout);
1126     return eIO_Success;
1127 }
1128 
1129 
1130 #ifdef IMPLEMENTED__CONN_WaitAsync
1131 static EIO_Status s_VT_WaitAsync
1132 (void*                   connector,
1133  FConnectorAsyncHandler  func,
1134  SConnectorAsyncHandler* data)
1135 {
1136     return eIO_NotSupported;
1137 }
1138 #endif
1139 
1140 
1141 static void s_Setup(SMetaConnector *meta, CONNECTOR connector)
1142 {
1143     SHttpConnector* uuu = (SHttpConnector*) connector->handle;
1144 
1145     /* initialize virtual table */
1146     CONN_SET_METHOD(meta, get_type,   s_VT_GetType,   connector);
1147     CONN_SET_METHOD(meta, descr,      s_VT_Descr,     connector);
1148     CONN_SET_METHOD(meta, open,       s_VT_Open,      connector);
1149     CONN_SET_METHOD(meta, wait,       s_VT_Wait,      connector);
1150     CONN_SET_METHOD(meta, write,      s_VT_Write,     connector);
1151     CONN_SET_METHOD(meta, flush,      s_VT_Flush,     connector);
1152     CONN_SET_METHOD(meta, read,       s_VT_Read,      connector);
1153     CONN_SET_METHOD(meta, status,     s_VT_Status,    connector);
1154     CONN_SET_METHOD(meta, close,      s_VT_Close,     connector);
1155 #ifdef IMPLEMENTED__CONN_WaitAsync
1156     CONN_SET_METHOD(meta, wait_async, s_VT_WaitAsync, connector);
1157 #endif
1158     CONN_SET_DEFAULT_TIMEOUT(meta, uuu->net_info->timeout);
1159 }
1160 
1161 
1162 static void s_Destroy(CONNECTOR connector)
1163 {
1164     SHttpConnector* uuu = (SHttpConnector*) connector->handle;
1165 
1166     if (uuu->adjust_cleanup)
1167         uuu->adjust_cleanup(uuu->adjust_data);
1168     ConnNetInfo_Destroy(uuu->net_info);
1169     BUF_Destroy(uuu->http);
1170     BUF_Destroy(uuu->r_buf);
1171     BUF_Destroy(uuu->w_buf);
1172     free(uuu);
1173     connector->handle = 0;
1174     free(connector);
1175 }
1176 
1177 
1178 /* NB: HTTP tag name misspelled as "Referer" as per the standard. */
1179 static void s_AddReferer(SConnNetInfo* net_info)
1180 {
1181     const char* s;
1182     char* referer;
1183     if (!net_info  ||  !net_info->http_referer  ||  !*net_info->http_referer)
1184         return;
1185     if ((s = net_info->http_user_header) != 0) {
1186         int/*bool*/ first;
1187         for (first = 1;  *s;  first = 0) {
1188             if (strncasecmp(s++, "\nReferer: " + first, 10 - first) == 0)
1189                 return;
1190             if (!(s = strchr(s, '\n')))
1191                 break;
1192         }
1193     }
1194     if (!(referer = (char*) malloc(strlen(net_info->http_referer) + 12)))
1195         return;
1196     sprintf(referer, "Referer: %s\r\n", net_info->http_referer);
1197     ConnNetInfo_ExtendUserHeader(net_info, referer);
1198     free(referer);
1199 }
1200 
1201 
1202 static CONNECTOR s_CreateConnector
1203 (const SConnNetInfo*  net_info,
1204  const char*          user_header,
1205  THCC_Flags           flags,
1206  FHttpParseHTTPHeader parse_http_hdr,
1207  FHttpAdjustNetInfo   adjust_net_info,
1208  void*                adjust_data,
1209  FHttpAdjustCleanup   adjust_cleanup)
1210 {
1211     char            value[32];
1212     CONNECTOR       ccc;
1213     SHttpConnector* uuu;
1214     SConnNetInfo*   xxx;
1215     char*           fff;
1216 
1217     xxx = net_info ? ConnNetInfo_Clone(net_info) : ConnNetInfo_Create(0);
1218     if (!xxx)
1219         return 0;
1220     if ((flags & fHCC_NoAutoRetry)  ||  !xxx->max_try)
1221         xxx->max_try = 1;
1222 
1223     if ((fff = strchr(xxx->args, '#')) != 0)
1224         *fff = '\0';
1225     if ((xxx->scheme != eURL_Unspec  && 
1226          xxx->scheme != eURL_Https   &&
1227          xxx->scheme != eURL_Http)   ||
1228         !ConnNetInfo_AdjustForHttpProxy(xxx)) {
1229         ConnNetInfo_Destroy(xxx);
1230         return 0;
1231     }
1232     if (xxx->scheme == eURL_Unspec)
1233         xxx->scheme = eURL_Http;
1234 
1235     if (!(ccc = (SConnector    *) malloc(sizeof(SConnector    )))  ||
1236         !(uuu = (SHttpConnector*) malloc(sizeof(SHttpConnector)))) {
1237         if (ccc)
1238             free(ccc);
1239         ConnNetInfo_Destroy(xxx);
1240         return 0;
1241     }
1242     if (user_header)
1243         ConnNetInfo_OverrideUserHeader(xxx, user_header);
1244     s_AddReferer(xxx);
1245 
1246     ConnNetInfo_GetValue(0, "HTTP_INSECURE_REDIRECT", value, sizeof(value),"");
1247     if (*value  &&  (strcmp    (value, "1")    == 0  ||
1248                      strcasecmp(value, "on")   == 0  ||
1249                      strcasecmp(value, "yes")  == 0  ||
1250                      strcasecmp(value, "true") == 0)) {
1251         flags |= fHCC_InsecureRedirect;
1252     }
1253 
1254     /* initialize internal data structure */
1255     uuu->net_info         = xxx;
1256 
1257     uuu->parse_http_hdr   = parse_http_hdr;
1258     uuu->adjust_net_info  = adjust_net_info;
1259     uuu->adjust_cleanup   = adjust_cleanup;
1260     uuu->adjust_data      = adjust_data;
1261 
1262     uuu->flags            = flags;
1263     uuu->reserved         = 0;
1264     uuu->can_connect      = eCC_Once;         /* will be properly set at open*/
1265 
1266     ConnNetInfo_GetValue(0, "HTTP_ERROR_HEADER_ONLY", value, sizeof(value),"");
1267     uuu->error_header = (*value  &&  (strcmp    (value, "1")    == 0  ||
1268                                       strcasecmp(value, "on")   == 0  ||
1269                                       strcasecmp(value, "yes")  == 0  ||
1270                                       strcasecmp(value, "true") == 0));
1271 
1272     uuu->sock             = 0;
1273     uuu->o_timeout        = kDefaultTimeout;  /* deliberately bad values --  */
1274     uuu->w_timeout        = kDefaultTimeout;  /* must be reset prior to use  */
1275     uuu->http             = 0;
1276     uuu->r_buf            = 0;
1277     uuu->w_buf            = 0;
1278     /* there are some unintialized fields left -- they are initted later */
1279 
1280     /* initialize connector structure */
1281     ccc->handle  = uuu;
1282     ccc->next    = 0;
1283     ccc->meta    = 0;
1284     ccc->setup   = s_Setup;
1285     ccc->destroy = s_Destroy;
1286 
1287     return ccc;
1288 }
1289 
1290 
1291 /***********************************************************************
1292  *  EXTERNAL -- the connector's "constructor"
1293  ***********************************************************************/
1294 
1295 extern CONNECTOR HTTP_CreateConnector
1296 (const SConnNetInfo* net_info,
1297  const char*         user_header,
1298  THCC_Flags          flags)
1299 {
1300     return s_CreateConnector(net_info, user_header, flags, 0, 0, 0, 0);
1301 }
1302 
1303 
1304 extern CONNECTOR HTTP_CreateConnectorEx
1305 (const SConnNetInfo*  net_info,
1306  THCC_Flags           flags,
1307  FHttpParseHTTPHeader parse_http_hdr,
1308  FHttpAdjustNetInfo   adjust_net_info,
1309  void*                adjust_data,
1310  FHttpAdjustCleanup   adjust_cleanup)
1311 {
1312     return s_CreateConnector(net_info, 0, flags, parse_http_hdr,
1313                              adjust_net_info, adjust_data, adjust_cleanup);
1314 }
1315 
1316 
1317 extern void HTTP_SetNcbiMessageHook(FHTTP_NcbiMessageHook hook)
1318 {
1319     if (hook) {
1320         if (hook != s_MessageHook)
1321             s_MessageIssued = s_MessageIssued ? -1 : -2;
1322     } else if (s_MessageIssued < -1)
1323         s_MessageIssued = 0;
1324     s_MessageHook = hook;
1325 }
1326 

source navigation ]   [ diff markup ]   [ identifier search ]   [ freetext search ]   [ file search ]  

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.