src/connect/ncbi_http_connector.c

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

Generated on Wed Dec 9 04:13:51 2009 for NCBI C++ ToolKit by  doxygen 1.4.6
Modified on Wed Dec 09 08:17:55 2009 by modify_doxy.py rev. 173732