|
NCBI Home IEB Home C Toolkit docs C++ Toolkit source browser C Toolkit source browser (2) |
NCBI C Toolkit Cross ReferenceC/connect/ncbi_http_connector.c |
source navigation diff markup identifier search freetext search file search |
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 |
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more information. |