1 /* $Id: ncbi_service_connector.c,v 6.152 2016/12/30 17:29:14 fukanchi 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
27 *
28 * File Description:
29 * Implementation of CONNECTOR to a named service
30 *
31 */
32
33 #include "ncbi_ansi_ext.h"
34 #include "ncbi_comm.h"
35 #include "ncbi_priv.h"
36 #include "ncbi_servicep.h"
37 #include "ncbi_socketp.h"
38 #include <connect/ncbi_service_connector.h>
39 #include <connect/ncbi_socket_connector.h>
40 #include <ctype.h>
41 #include <stdlib.h>
42
43 #define NCBI_USE_ERRCODE_X Connect_Service
44
45
46 typedef struct SServiceConnectorTag {
47 const char* type; /* Verbal connector type */
48 const char* descr; /* Verbal connector description */
49 SConnNetInfo* net_info; /* Connection information */
50 const char* user_header; /* User header currently set */
51 SERV_ITER iter; /* Dispatcher information */
52 SMetaConnector meta; /* Low level comm.conn and its VT */
53 unsigned int host; /* Parsed connection info... (n.b.o) */
54 unsigned short port; /* ... (h.b.o) */
55 unsigned short retry; /* Open retry count since last okay */
56 TSERV_TypeOnly types; /* Server types w/o any specials */
57 unsigned reset:1; /* Non-zero if iter was just reset */
58 SSERVICE_Extra extra; /* Extra params as passed to ctor */
59 ticket_t ticket; /* Network byte order (none if zero) */
60 EIO_Status status; /* Status of last op */
61 const char service[1]; /* Untranslated (orig.) service name */
62 } SServiceConnector;
63
64
65 /***********************************************************************
66 * INTERNAL -- "s_VT_*" functions for the "virt. table" of connector methods
67 ***********************************************************************/
68
69 #ifdef __cplusplus
70 extern "C" {
71 #endif /*__cplusplus*/
72 static const char* s_VT_GetType(CONNECTOR connector);
73 static char* s_VT_Descr (CONNECTOR connector);
74 static EIO_Status s_VT_Open (CONNECTOR connector,
75 const STimeout* timeout);
76 static EIO_Status s_VT_Status (CONNECTOR connector,
77 EIO_Event direction);
78 static EIO_Status s_VT_Close (CONNECTOR connector,
79 const STimeout* timeout);
80 static void s_Setup (CONNECTOR connector);
81 static void s_Destroy (CONNECTOR connector);
82 #ifdef __cplusplus
83 } /* extern "C" */
84 #endif /*__cplusplus*/
85
86
s_OpenDispatcher(SServiceConnector * uuu)87 static int/*bool*/ s_OpenDispatcher(SServiceConnector* uuu)
88 {
89 TSERV_Type types = uuu->types;
90 if (uuu->net_info->stateless)
91 types |= fSERV_Stateless;
92 if (!(uuu->iter = SERV_Open(uuu->service, types,
93 SERV_LOCALHOST, uuu->net_info))) {
94 CORE_LOGF_X(5, eLOG_Error,
95 ("[%s] Service not found", uuu->service));
96 return 0/*false*/;
97 }
98 uuu->reset = 1/*true*/;
99 return 1/*true*/;
100 }
101
102
s_CloseDispatcher(SServiceConnector * uuu)103 static void s_CloseDispatcher(SServiceConnector* uuu)
104 {
105 SERV_Close(uuu->iter);
106 uuu->iter = 0;
107 }
108
109
110 /* Reset functions, which are implemented only in transport
111 * connectors, but not in this connector.
112 */
s_Reset(SMetaConnector * meta,CONNECTOR connector)113 static void s_Reset(SMetaConnector *meta, CONNECTOR connector)
114 {
115 CONN_SET_METHOD(meta, descr, s_VT_Descr, connector);
116 CONN_SET_METHOD(meta, wait, 0, 0);
117 CONN_SET_METHOD(meta, write, 0, 0);
118 CONN_SET_METHOD(meta, flush, 0, 0);
119 CONN_SET_METHOD(meta, read, 0, 0);
120 CONN_SET_METHOD(meta, status, s_VT_Status, connector);
121 }
122
123
s_ParseHeader(const char * header,void * user_data,int server_error,int user_callback_enabled)124 static EHTTP_HeaderParse s_ParseHeader(const char* header,
125 void* user_data,
126 int server_error,
127 int/*bool*/ user_callback_enabled)
128 {
129 static const char kStateless[] = "TRY_STATELESS";
130 static const size_t kSLen = sizeof(kStateless) - 1;
131 SServiceConnector* uuu = (SServiceConnector*) user_data;
132 EHTTP_HeaderParse header_parse;
133
134 SERV_Update(uuu->iter, header, server_error);
135 if (user_callback_enabled && uuu->extra.parse_header) {
136 header_parse
137 = uuu->extra.parse_header(header, uuu->extra.data, server_error);
138 if (server_error || !header_parse)
139 return header_parse;
140 } else {
141 if (server_error)
142 return eHTTP_HeaderSuccess;
143 header_parse = eHTTP_HeaderError;
144 }
145
146 while (header && *header) {
147 if (strncasecmp(header, HTTP_CONNECTION_INFO,
148 sizeof(HTTP_CONNECTION_INFO) - 1) == 0) {
149 if (uuu->host)
150 break/*failed - duplicate connection info*/;
151 header += sizeof(HTTP_CONNECTION_INFO) - 1;
152 while (*header && isspace((unsigned char)(*header)))
153 header++;
154 if (strncasecmp(header, kStateless, kSLen) == 0 &&
155 (!header[kSLen] || isspace((unsigned char) header[kSLen]))) {
156 /* Special keyword for switching into stateless mode */
157 uuu->host = (unsigned int)(-1);
158 #if defined(_DEBUG) && !defined(NDEBUG)
159 if (uuu->net_info->debug_printout) {
160 CORE_LOGF_X(2, eLOG_Note,
161 ("[%s] Fallback to stateless", uuu->service));
162 }
163 #endif /*_DEBUG && !NDEBUG*/
164 } else {
165 unsigned int i1, i2, i3, i4, tkt, n, m;
166 unsigned char o1, o2, o3, o4;
167 char ipaddr[40];
168
169 if (sscanf(header, "%u.%u.%u.%u%n", &i1, &i2, &i3, &i4, &n) < 4
170 || sscanf(header + n, "%hu%x%n", &uuu->port, &tkt, &m) < 2
171 || (header[m += n] && !isspace((unsigned char)header[m]))){
172 break/*failed - unreadable connection info*/;
173 }
174 o1 = i1; o2 = i2; o3 = i3; o4 = i4;
175 sprintf(ipaddr, "%u.%u.%u.%u", o1, o2, o3, o4);
176 if (strncmp(header, ipaddr, n) != 0
177 || !(uuu->host = SOCK_gethostbyname(ipaddr))
178 || !uuu->port) {
179 break/*failed - bad host:port in connection info*/;
180 }
181 uuu->ticket = SOCK_HostToNetLong(tkt);
182 }
183 }
184 if ((header = strchr(header, '\n')) != 0)
185 header++;
186 }
187
188 if (header && *header)
189 uuu->host = 0;
190 else if (!header_parse)
191 header_parse = eHTTP_HeaderSuccess;
192 return header_parse;
193 }
194
195
196 #ifdef __cplusplus
197 extern "C" {
198 #endif /*__cplusplus*/
199 static EHTTP_HeaderParse
s_ParseHeaderUCB(const char * header,void * data,int server_error)200 s_ParseHeaderUCB (const char* header, void* data, int server_error)
201 {
202 return s_ParseHeader(header, data, server_error, 1/*enable user CB*/);
203 }
204 #ifdef __cplusplus
205 }
206 #endif /*__cplusplus*/
207
208
209 #ifdef __cplusplus
210 extern "C" {
211 #endif /*__cplusplus*/
212 static EHTTP_HeaderParse
s_ParseHeaderNoUCB(const char * header,void * data,int server_error)213 s_ParseHeaderNoUCB(const char* header, void* data, int server_error)
214 {
215 return s_ParseHeader(header, data, server_error, 0/*disable user CB*/);
216 }
217 #ifdef __cplusplus
218 }
219 #endif /*__cplusplus*/
220
221
222 /*ARGSUSED*/
s_IsContentTypeDefined(const char * service,const SConnNetInfo * net_info,EMIME_Type mime_t,EMIME_SubType mime_s,EMIME_Encoding mime_e)223 static int/*bool*/ s_IsContentTypeDefined(const char* service,
224 const SConnNetInfo* net_info,
225 EMIME_Type mime_t,
226 EMIME_SubType mime_s,
227 EMIME_Encoding mime_e)
228 {
229 const char* s;
230
231 assert(net_info);
232 for (s = net_info->http_user_header; s; s = strchr(s, '\n')) {
233 if (s != net_info->http_user_header)
234 s++;
235 if (!*s)
236 break;
237 if (strncasecmp(s, "content-type: ", 14) == 0) {
238 #if defined(_DEBUG) && !defined(NDEBUG)
239 EMIME_Type m_t;
240 EMIME_SubType m_s;
241 EMIME_Encoding m_e;
242 char c_t[MAX_CONTENT_TYPE_LEN];
243 if (net_info->debug_printout &&
244 mime_t != eMIME_T_Undefined &&
245 mime_t != eMIME_T_Unknown &&
246 (!MIME_ParseContentTypeEx(s, &m_t, &m_s, &m_e)
247 || mime_t != m_t
248 || (mime_s != eMIME_Undefined &&
249 mime_s != eMIME_Unknown &&
250 m_s != eMIME_Unknown && mime_s != m_s)
251 || (mime_e != eENCOD_None &&
252 m_e != eENCOD_None && mime_e != m_e))) {
253 const char* c;
254 size_t len;
255 char* t;
256 for (s += 15; *s; s++) {
257 if (!isspace((unsigned char)(*s)))
258 break;
259 }
260 if (!(c = strchr(s, '\n')))
261 c = s + strlen(s);
262 if (c > s && c[-1] == '\r')
263 c--;
264 len = (size_t)(c - s);
265 if ((t = (char*) malloc(len + 1)) != 0) {
266 memcpy(t, s, len);
267 t[len] = '\0';
268 }
269 if (!MIME_ComposeContentTypeEx(mime_t, mime_s, mime_e,
270 c_t, sizeof(c_t))) {
271 *c_t = '\0';
272 }
273 CORE_LOGF_X(3, eLOG_Warning,
274 ("[%s] Content-Type mismatch: "
275 "%s%s%s%s%s%s%s", service,
276 t && *t ? "specified=<" : "",
277 t && *t ? t : "",
278 t && *t ? ">" : "",
279 t && *t && *c_t ? ", " : "",
280 *c_t ? "configured=<" : "",
281 *c_t ? c_t : "",
282 *c_t ? ">" : ""));
283 if (t)
284 free(t);
285 }
286 #endif /*_DEBUG && !NDEBUG*/
287 return 1/*true*/;
288 }
289 }
290 return 0/*false*/;
291 }
292
293
s_AdjustNetParams(const char * service,SConnNetInfo * net_info,EReqMethod req_method,const char * cgi_path,const char * cgi_args,const char * args,const char * static_header,EMIME_Type mime_t,EMIME_SubType mime_s,EMIME_Encoding mime_e,const char * extend_header)294 static const char* s_AdjustNetParams(const char* service,
295 SConnNetInfo* net_info,
296 EReqMethod req_method,
297 const char* cgi_path,
298 const char* cgi_args,
299 const char* args,
300 const char* static_header,
301 EMIME_Type mime_t,
302 EMIME_SubType mime_s,
303 EMIME_Encoding mime_e,
304 const char* extend_header)
305 {
306 const char *retval = 0;
307
308 net_info->req_method = req_method;
309
310 if (cgi_path)
311 strncpy0(net_info->path, cgi_path, sizeof(net_info->path) - 1);
312
313 if (args)
314 strncpy0(net_info->args, args, sizeof(net_info->args) - 1);
315 ConnNetInfo_DeleteAllArgs(net_info, cgi_args);
316
317 if (ConnNetInfo_PrependArg(net_info, cgi_args, 0)) {
318 size_t sh_len = static_header ? strlen(static_header) : 0;
319 size_t eh_len = extend_header ? strlen(extend_header) : 0;
320 char c_t[MAX_CONTENT_TYPE_LEN];
321 size_t len;
322
323 if (s_IsContentTypeDefined(service, net_info, mime_t, mime_s, mime_e)
324 || !MIME_ComposeContentTypeEx(mime_t, mime_s, mime_e,
325 c_t, sizeof(c_t))) {
326 *c_t = '\0';
327 len = 0;
328 } else
329 len = strlen(c_t);
330 if ((len += sh_len + eh_len) != 0) {
331 char* temp = (char*) malloc(++len/*w/EOL*/);
332 if (temp) {
333 retval = temp;
334 if (static_header) {
335 memcpy(temp, static_header, sh_len);
336 temp += sh_len;
337 }
338 if (extend_header) {
339 memcpy(temp, extend_header, eh_len);
340 temp += eh_len;
341 }
342 strcpy(temp, c_t);
343 assert(*retval);
344 }
345 } else
346 retval = "";
347 }
348
349 return retval;
350 }
351
352
s_GetNextInfo(SServiceConnector * uuu,int http)353 static SSERV_InfoCPtr s_GetNextInfo(SServiceConnector* uuu, int/*bool*/ http)
354 {
355 for (;;) {
356 SSERV_InfoCPtr info = uuu->extra.get_next_info
357 ? uuu->extra.get_next_info(uuu->extra.data, uuu->iter)
358 : SERV_GetNextInfo(uuu->iter);
359 if (info) {
360 if (http) {
361 /* Skip any 'stateful_capable' or unconnectable entries here,
362 * which might have been left behind by either
363 * a/ a failed stateful dispatching that fallen back to
364 * stateless HTTP mode, or
365 * b/ a too relaxed server type selection.
366 */
367 if ((info->mode & fSERV_Stateful) || info->type == fSERV_Dns)
368 continue;
369 }
370 uuu->reset = 0/*false*/;
371 return info;
372 }
373 if (uuu->reset)
374 break;
375 SERV_Reset(uuu->iter);
376 uuu->reset = 1/*true*/;
377 }
378 return 0;
379 }
380
381
x_HostPort(const char * host,unsigned short nport)382 static char* x_HostPort(const char* host, unsigned short nport)
383 {
384 char* hostport, port[16];
385 size_t hostlen = strlen(host);
386 size_t portlen = (size_t) sprintf(port, ":%hu", nport) + 1;
387 hostport = (char*) malloc(hostlen + portlen);
388 if (hostport) {
389 memcpy(hostport, host, hostlen);
390 memcpy(hostport + hostlen, port, portlen);
391 }
392 return hostport;
393 }
394
395
396 /* Until r294766, this code used to send a ticket along with building the
397 * tunnel, but for buggy proxies, which ignore HTTP body as connection data
398 * (and thus violate the standard), that shortcut could not be utilized;
399 * so the longer multi-step sequence was introduced below, instead.
400 * Cf. ncbi_conn_stream.cpp: s_SocketConnectorBuilder().
401 */
s_SocketConnectorBuilder(SConnNetInfo * net_info,const char * hostport,EIO_Status * status,const void * data,size_t size,TSOCK_Flags flags)402 static CONNECTOR s_SocketConnectorBuilder(SConnNetInfo* net_info,
403 const char* hostport,
404 EIO_Status* status,
405 const void* data,
406 size_t size,
407 TSOCK_Flags flags)
408 {
409 int/*bool*/ proxy = 0/*false*/;
410 SOCK sock = 0, s;
411 SSOCK_Init init;
412 CONNECTOR c;
413
414 flags |= (net_info->debug_printout == eDebugPrintout_Data
415 ? fSOCK_LogOn : fSOCK_LogDefault);
416 if (net_info->http_proxy_host[0] && net_info->http_proxy_port) {
417 /* NB: ideally, should have pushed data:size here if proxy not buggy */
418 *status = HTTP_CreateTunnel(net_info, fHTTP_NoAutoRetry, &sock);
419 assert(!sock ^ !(*status != eIO_Success));
420 if (*status == eIO_Success
421 && (size || (flags & ~(fSOCK_LogOn | fSOCK_LogDefault)))) {
422 /* push initial data through the proxy, as-is (i.e. clear-text) */
423 TSOCK_Flags tempf = flags;
424 if (size && (flags & fSOCK_Secure)) {
425 tempf &= fSOCK_LogOn | fSOCK_LogDefault;
426 tempf &= ~fSOCK_Secure;
427 }
428 memset(&init, 0, sizeof(init));
429 init.data = data;
430 init.size = size;
431 init.cred = 0;
432 *status = SOCK_CreateOnTopInternal(sock, 0, &s,
433 &init, tempf);
434 assert(!s ^ !(*status != eIO_Success));
435 SOCK_Destroy(sock);
436 sock = s;
437 if (*status == eIO_Success && tempf != flags) {
438 init.data = 0;
439 init.size = 0;
440 init.cred = net_info->credentials;
441 *status = SOCK_CreateOnTopInternal(sock, 0, &s,
442 &init, flags);
443 assert(!s ^ !(*status != eIO_Success));
444 SOCK_Destroy(sock);
445 sock = s;
446 }
447 }
448 proxy = 1/*true*/;
449 }
450 if (!sock && (!proxy || net_info->http_proxy_leak)) {
451 TSOCK_Flags tempf = flags;
452 if (size && (flags & fSOCK_Secure)) {
453 tempf &= fSOCK_LogOn | fSOCK_LogDefault;
454 tempf &= ~fSOCK_Secure;
455 }
456 if (!proxy && net_info->debug_printout) {
457 net_info->scheme = eURL_Unspec;
458 net_info->req_method = eReqMethod_Any;
459 net_info->firewall = 0;
460 net_info->stateless = 0;
461 net_info->lb_disable = 0;
462 net_info->user[0] = '\0';
463 net_info->pass[0] = '\0';
464 net_info->path[0] = '\0';
465 net_info->args[0] = '\0';
466 net_info->http_proxy_host[0] = '\0';
467 net_info->http_proxy_port = 0;
468 net_info->http_proxy_user[0] = '\0';
469 net_info->http_proxy_pass[0] = '\0';
470 ConnNetInfo_SetUserHeader(net_info, 0);
471 if (net_info->http_referer) {
472 free((void*) net_info->http_referer);
473 net_info->http_referer = 0;
474 }
475 ConnNetInfo_Log(net_info, eLOG_Note, CORE_GetLOG());
476 }
477 memset(&init, 0, sizeof(init));
478 init.data = data;
479 init.size = size;
480 init.cred = 0;
481 *status = SOCK_CreateInternal(net_info->host, net_info->port,
482 net_info->timeout, &sock,
483 &init, tempf);
484 assert(!sock ^ !(*status != eIO_Success));
485 if (*status == eIO_Success && tempf != flags) {
486 init.data = 0;
487 init.size = 0;
488 init.cred = net_info->credentials;
489 *status = SOCK_CreateOnTopInternal(sock, 0, &s,
490 &init, flags);
491 assert(!s ^ !(*status != eIO_Success));
492 SOCK_Destroy(sock);
493 sock = s;
494 }
495 }
496 if (!(c = SOCK_CreateConnectorOnTopEx(sock, 1/*own*/, hostport))) {
497 if (*status == eIO_Success)
498 *status = eIO_Unknown;
499 SOCK_Abort(sock);
500 SOCK_Close(sock);
501 }
502 return c;
503 }
504
505
506 /* Although all additional HTTP tags that comprise the dispatching have their
507 * default values, which in most cases are fine with us, we will use these tags
508 * explicitly to distinguish the calls originated within the service connector
509 * from other calls (e.g. by Web browsers), and let the dispatcher decide
510 * whether to use more expensive dispatching (involving loopback connections)
511 * in the latter case.
512 */
513
514
515 #ifdef __cplusplus
516 extern "C" {
517 static int s_Adjust(SConnNetInfo*, void*, unsigned int);
518 }
519 #endif /*__cplusplus*/
520
521 /*ARGSUSED*/
522 /* NB: This callback is only for services called via direct HTTP */
s_Adjust(SConnNetInfo * net_info,void * data,unsigned int n)523 static int/*bool*/ s_Adjust(SConnNetInfo* net_info,
524 void* data,
525 unsigned int n)
526 {
527 SServiceConnector* uuu = (SServiceConnector*) data;
528 const char* user_header;
529 char* iter_header;
530 SSERV_InfoCPtr info;
531
532 assert(n || uuu->extra.adjust);
533 assert(!net_info->firewall || net_info->stateless);
534
535 if (!n)
536 return uuu->extra.adjust(net_info, uuu->extra.data, 0);
537
538 if (uuu->retry >= uuu->net_info->max_try)
539 return 0/*failure - too many errors*/;
540 uuu->retry++;
541
542 if (!(info = s_GetNextInfo(uuu, 1/*http*/)))
543 return 0/*failure - not adjusted*/;
544
545 iter_header = SERV_Print(uuu->iter, 0, 0);
546 switch (info->type) {
547 case fSERV_Ncbid:
548 user_header = "Connection-Mode: STATELESS\r\n"; /*default*/
549 user_header = s_AdjustNetParams(uuu->service, net_info,
550 eReqMethod_Post,
551 NCBID_WEBPATH,
552 SERV_NCBID_ARGS(&info->u.ncbid),
553 uuu->net_info->args,
554 user_header, info->mime_t,
555 info->mime_s, info->mime_e,
556 iter_header);
557 break;
558 case fSERV_Http:
559 case fSERV_HttpGet:
560 case fSERV_HttpPost:
561 user_header = "Client-Mode: STATELESS_ONLY\r\n"; /*default*/
562 user_header = s_AdjustNetParams(uuu->service, net_info,
563 info->type == fSERV_HttpPost
564 ? eReqMethod_Post
565 : (info->type == fSERV_HttpGet
566 ? eReqMethod_Get
567 : eReqMethod_Any),
568 SERV_HTTP_PATH(&info->u.http),
569 SERV_HTTP_ARGS(&info->u.http),
570 uuu->net_info->args,
571 user_header, info->mime_t,
572 info->mime_s, info->mime_e,
573 iter_header);
574 break;
575 case fSERV_Firewall:
576 case fSERV_Standalone:
577 user_header = "Client-Mode: STATELESS_ONLY\r\n"; /*default*/
578 user_header = s_AdjustNetParams(uuu->service, net_info,
579 eReqMethod_Any,
580 uuu->net_info->path, 0,
581 uuu->net_info->args,
582 user_header, info->mime_t,
583 info->mime_s, info->mime_e,
584 iter_header);
585 break;
586 default:
587 user_header = 0;
588 assert(0);
589 break;
590 }
591 if (iter_header)
592 free(iter_header);
593 if (!user_header)
594 return 0/*false - not adjusted*/;
595
596 if (uuu->user_header) {
597 assert(*uuu->user_header);
598 ConnNetInfo_DeleteUserHeader(net_info, uuu->user_header);
599 free((void*) uuu->user_header);
600 }
601 if (*user_header) {
602 uuu->user_header = user_header;
603 if (!ConnNetInfo_OverrideUserHeader(net_info, user_header))
604 return 0/*failure - not adjusted*/;
605 } else /*NB: special case ""*/
606 uuu->user_header = 0;
607
608 if (info->type == fSERV_Ncbid || (info->type & fSERV_Http)) {
609 SOCK_ntoa(info->host, net_info->host, sizeof(net_info->host));
610 net_info->port = info->port;
611 } else {
612 strcpy(net_info->host, uuu->net_info->host);
613 net_info->port = uuu->net_info->port;
614 }
615 return uuu->extra.adjust
616 && !uuu->extra.adjust(net_info, uuu->extra.data, uuu->retry)
617 ? 0/*failure - not adjusted*/
618 : 1/*success - adjusted*/;
619 }
620
621
622 #ifdef __GNUC__
623 inline
624 #endif /*__GNUC__*/
x_DestroyConnector(CONNECTOR c)625 static void x_DestroyConnector(CONNECTOR c)
626 {
627 assert(!c->meta && !c->next);
628 if (c->destroy)
629 c->destroy(c);
630 }
631
632
s_Open(SServiceConnector * uuu,const STimeout * timeout,SSERV_InfoCPtr info,SConnNetInfo * net_info,EIO_Status * status)633 static CONNECTOR s_Open(SServiceConnector* uuu,
634 const STimeout* timeout,
635 SSERV_InfoCPtr info,
636 SConnNetInfo* net_info,
637 EIO_Status* status)
638 {
639 int/*bool*/ but_last = 0/*false*/;
640 const char* user_header; /* either static "" or non-empty dynamic string */
641 char* iter_header;
642 EReqMethod req_method;
643
644 if (info && info->type != fSERV_Firewall) {
645 /* Not a firewall/relay connection here */
646 /* We know the connection point, let's try to use it! */
647 if (info->type != fSERV_Standalone || !net_info->stateless) {
648 SOCK_ntoa(info->host, net_info->host, sizeof(net_info->host));
649 net_info->port = info->port;
650 }
651
652 switch (info->type) {
653 case fSERV_Ncbid:
654 /* Connection directly to NCBID, add NCBID-specific tags */
655 if (info->mode & fSERV_Secure)
656 net_info->scheme = eURL_Https;
657 if (net_info->stateless) {
658 /* Connection request with data */
659 user_header = "Connection-Mode: STATELESS\r\n"; /*default*/
660 req_method = eReqMethod_Post;
661 } else {
662 /* We will be waiting for conn-info back */
663 user_header = "Connection-Mode: STATEFUL\r\n";
664 req_method = eReqMethod_Get;
665 }
666 user_header = s_AdjustNetParams(uuu->service, net_info, req_method,
667 NCBID_WEBPATH,
668 SERV_NCBID_ARGS(&info->u.ncbid),
669 0, user_header, info->mime_t,
670 info->mime_s, info->mime_e, 0);
671 break;
672 case fSERV_Http:
673 case fSERV_HttpGet:
674 case fSERV_HttpPost:
675 /* Connection directly to CGI */
676 req_method = info->type == fSERV_HttpGet
677 ? eReqMethod_Get : (info->type == fSERV_HttpPost
678 ? eReqMethod_Post : eReqMethod_Any);
679 user_header = "Client-Mode: STATELESS_ONLY\r\n"; /*default*/
680 user_header = s_AdjustNetParams(uuu->service, net_info, req_method,
681 SERV_HTTP_PATH(&info->u.http),
682 SERV_HTTP_ARGS(&info->u.http),
683 0, user_header, info->mime_t,
684 info->mime_s, info->mime_e, 0);
685 break;
686 case fSERV_Standalone:
687 if (!net_info->stateless) {
688 assert(!uuu->descr);
689 uuu->descr = x_HostPort(net_info->host, net_info->port);
690 return s_SocketConnectorBuilder(net_info, uuu->descr, status,
691 0, 0, info->mode & fSERV_Secure
692 ? fSOCK_Secure : 0);
693 }
694 /* Otherwise, it will be a pass-thru connection via dispatcher */
695 user_header = "Client-Mode: STATELESS_ONLY\r\n"; /*default*/
696 user_header = s_AdjustNetParams(uuu->service, net_info,
697 eReqMethod_Any, 0, 0,
698 0, user_header, info->mime_t,
699 info->mime_s, info->mime_e, 0);
700 but_last = 1/*true*/;
701 break;
702 default:
703 user_header = 0;
704 assert(0);
705 break;
706 }
707 } else {
708 EMIME_Type mime_t;
709 EMIME_SubType mime_s;
710 EMIME_Encoding mime_e;
711 if (!net_info->scheme)
712 net_info->scheme = eURL_Https;
713 if (net_info->stateless
714 || (info && (info->u.firewall.type & fSERV_Http))) {
715 if (info) {
716 req_method = (info->u.firewall.type == fSERV_HttpGet
717 ? eReqMethod_Get
718 : (info->u.firewall.type == fSERV_HttpPost
719 ? eReqMethod_Post
720 : eReqMethod_Any));
721 net_info->stateless = 1/*true*/;
722 } else
723 req_method = eReqMethod_Any;
724 } else
725 req_method = eReqMethod_Get;
726 if (info) {
727 mime_t = info->mime_t;
728 mime_s = info->mime_s;
729 mime_e = info->mime_e;
730 } else {
731 mime_t = eMIME_T_Undefined;
732 mime_s = eMIME_Undefined;
733 mime_e = eENCOD_None;
734 }
735 /* Firewall/relay connection thru dispatcher, special tags */
736 user_header = (net_info->stateless
737 ? "Client-Mode: STATELESS_ONLY\r\n" /*default*/
738 : "Client-Mode: STATEFUL_CAPABLE\r\n");
739 user_header = s_AdjustNetParams(uuu->service, net_info, req_method,
740 0, 0, 0, user_header,
741 mime_t, mime_s, mime_e, 0);
742 if (info)
743 but_last = 1/*true*/;
744 }
745 if (!user_header) {
746 *status = eIO_Unknown;
747 return 0;
748 }
749
750 if ((iter_header = SERV_Print(uuu->iter, net_info, but_last)) != 0) {
751 size_t uh_len;
752 if ((uh_len = strlen(user_header)) > 0) {
753 char* ih;
754 size_t ih_len = strlen(iter_header);
755 if ((ih = (char*) realloc(iter_header, ++uh_len + ih_len)) != 0) {
756 memcpy(ih + ih_len, user_header, uh_len);
757 iter_header = ih;
758 }
759 free((void*) user_header);
760 }
761 user_header = iter_header;
762 } else if (!*user_header)
763 user_header = 0; /* special case of assignment of literal "" */
764
765 if (uuu->user_header) {
766 ConnNetInfo_DeleteUserHeader(net_info, uuu->user_header);
767 free((void*) uuu->user_header);
768 }
769 uuu->user_header = user_header;
770 if (user_header && !ConnNetInfo_OverrideUserHeader(net_info, user_header)){
771 *status = eIO_Unknown;
772 return 0;
773 }
774
775 ConnNetInfo_ExtendUserHeader
776 (net_info, "User-Agent: NCBIServiceConnector/"
777 NCBI_DISP_VERSION
778 #ifdef NCBI_CXX_TOOLKIT
779 " (CXX Toolkit)"
780 #else
781 " (C Toolkit)"
782 #endif /*NCBI_CXX_TOOLKIT*/
783 );
784
785 *status = eIO_Success;
786 if (!net_info->stateless && (!info ||
787 info->type == fSERV_Firewall ||
788 info->type == fSERV_Ncbid)) {
789 /* Auxiliary HTTP connector first */
790 EIO_Status temp = eIO_Success;
791 CONNECTOR c;
792 CONN conn;
793
794 /* Clear connection info */
795 uuu->host = 0;
796 uuu->port = 0;
797 uuu->ticket = 0;
798 net_info->max_try = 1;
799 c = HTTP_CreateConnectorEx(net_info, fHTTP_Flushable,
800 s_ParseHeaderNoUCB, uuu/*user_data*/,
801 0/*adjust*/, 0/*cleanup*/);
802 /* Wait for connection info back from dispatcher */
803 if (c && (temp = CONN_Create(c, &conn)) == eIO_Success) {
804 CONN_SetTimeout(conn, eIO_Open, timeout);
805 CONN_SetTimeout(conn, eIO_ReadWrite, timeout);
806 CONN_SetTimeout(conn, eIO_Close, timeout);
807 /* Send all the HTTP data... */
808 if ((temp = CONN_Flush(conn)) != eIO_Success)
809 *status = temp;
810 /* ...then trigger the header callback */
811 if ((temp = CONN_Close(conn)) != eIO_Success &&
812 temp != eIO_Closed &&
813 *status < temp) {
814 *status = temp;
815 }
816 } else {
817 /* can only happen if we're out of memory */
818 const char* error;
819 if (c) {
820 error = IO_StatusStr(temp);
821 x_DestroyConnector(c);
822 *status = temp;
823 } else
824 error = 0;
825 CORE_LOGF_X(4, eLOG_Error,
826 ("[%s] Unable to create auxiliary HTTP %s%s%s",
827 uuu->service, c ? "connection" : "connector",
828 error && *error ? ": " : "", error ? error : ""));
829 assert(!uuu->host);
830 }
831 if (uuu->host == (unsigned int)(-1)) {
832 assert(!info || info->type == fSERV_Firewall);
833 assert(!net_info->stateless);
834 net_info->stateless = 1/*true*/;
835 /* Fallback to try to use stateless mode instead */
836 return s_Open(uuu, timeout, info, net_info, status);
837 }
838 if (!uuu->host || !uuu->port) {
839 /* no connection info found */
840 if (*status == eIO_Success)
841 *status = eIO_Unknown;
842 if (!net_info->scheme)
843 net_info->scheme = eURL_Http;
844 net_info->args[0] = '\0';
845 assert(!uuu->descr);
846 uuu->descr = ConnNetInfo_URL(net_info);
847 return 0;
848 }
849 if (net_info->firewall == eFWMode_Fallback
850 && !SERV_IsFirewallPort(uuu->port)) {
851 CORE_LOGF_X(9, eLOG_Warning,
852 ("[%s] Firewall port :%hu is not in the fallback set",
853 uuu->service, uuu->port));
854 }
855 ConnNetInfo_DeleteUserHeader(net_info, uuu->user_header);
856 SOCK_ntoa(uuu->host, net_info->host, sizeof(net_info->host));
857 net_info->port = uuu->port;
858 assert(!uuu->descr);
859 uuu->descr = x_HostPort(net_info->host, net_info->port);
860 if (net_info->http_proxy_host[0] && net_info->http_proxy_port)
861 net_info->scheme = uuu->net_info->scheme;
862 return s_SocketConnectorBuilder(net_info, uuu->descr, status,
863 &uuu->ticket,
864 uuu->ticket ? sizeof(uuu->ticket) : 0,
865 info && (info->mode & fSERV_Secure)
866 ? fSOCK_Secure : 0);
867 }
868 ConnNetInfo_DeleteUserHeader(net_info, "Host:");
869 if (info && (info->mode & fSERV_Secure))
870 net_info->scheme = eURL_Https;
871 else if (!net_info->scheme)
872 net_info->scheme = eURL_Http;
873 assert(!uuu->descr);
874 uuu->descr = ConnNetInfo_URL(net_info);
875 return !uuu->extra.adjust
876 || uuu->extra.adjust(net_info, uuu->extra.data, (unsigned int)(-1))
877 ? HTTP_CreateConnectorEx(net_info,
878 (uuu->extra.flags
879 & (fHTTP_Flushable |
880 fHTTP_NoAutoRetry |
881 (uuu->extra.adjust
882 ? fHTTP_AdjustOnRedirect
883 : 0)))
884 | fHTTP_AutoReconnect,
885 s_ParseHeaderUCB, uuu/*user_data*/,
886 s_Adjust, 0/*cleanup*/)
887 : 0/*failure*/;
888 }
889
890
s_Cleanup(SServiceConnector * uuu)891 static void s_Cleanup(SServiceConnector* uuu)
892 {
893 if (uuu->type) {
894 free((void*) uuu->type);
895 uuu->type = 0;
896 }
897 if (uuu->descr) {
898 free((void*) uuu->descr);
899 uuu->descr = 0;
900 }
901 if (uuu->user_header) {
902 free((void*) uuu->user_header);
903 uuu->user_header = 0;
904 }
905 }
906
907
s_Close(CONNECTOR connector,const STimeout * timeout,int cleanup)908 static EIO_Status s_Close(CONNECTOR connector,
909 const STimeout* timeout,
910 int/*bool*/ cleanup)
911 {
912 SServiceConnector* uuu = (SServiceConnector*) connector->handle;
913 EIO_Status status;
914
915 if (cleanup) {
916 status = uuu->meta.close
917 ? uuu->meta.close(uuu->meta.c_close, timeout)
918 : eIO_Success;
919 if (uuu->extra.reset)
920 uuu->extra.reset(uuu->extra.data);
921 s_CloseDispatcher(uuu);
922 s_Cleanup(uuu);
923 } else
924 status = eIO_Success/*unused*/;
925
926 if (uuu->meta.list) {
927 SMetaConnector* meta = connector->meta;
928 METACONN_Remove(meta, uuu->meta.list);
929 uuu->meta.list = 0;
930 s_Reset(meta, connector);
931 }
932
933 return status;
934 }
935
936
s_VT_GetType(CONNECTOR connector)937 static const char* s_VT_GetType(CONNECTOR connector)
938 {
939 SServiceConnector* uuu = (SServiceConnector*) connector->handle;
940 return uuu->type ? uuu->type : uuu->service;
941 }
942
943
s_VT_Descr(CONNECTOR connector)944 static char* s_VT_Descr(CONNECTOR connector)
945 {
946 SServiceConnector* uuu = (SServiceConnector*) connector->handle;
947 return uuu->descr && *uuu->descr ? strdup(uuu->descr) : 0;
948 }
949
950
s_VT_Open(CONNECTOR connector,const STimeout * timeout)951 static EIO_Status s_VT_Open(CONNECTOR connector, const STimeout* timeout)
952 {
953 SServiceConnector* uuu = (SServiceConnector*) connector->handle;
954 SMetaConnector* meta = connector->meta;
955 EIO_Status status = eIO_Closed;
956
957 for (uuu->retry = 0; uuu->retry < uuu->net_info->max_try; uuu->retry++) {
958 SConnNetInfo* net_info;
959 SSERV_InfoCPtr info;
960 int stateless;
961 CONNECTOR c;
962
963 assert(!uuu->meta.list && status != eIO_Success);
964
965 if (!uuu->iter && !s_OpenDispatcher(uuu))
966 break;
967
968 if (!(info = s_GetNextInfo(uuu, 0/*any*/))
969 && (!uuu->net_info->firewall
970 || strcasecmp(SERV_MapperName(uuu->iter), "local") == 0)) {
971 break;
972 }
973 if (uuu->type) {
974 free((void*) uuu->type);
975 uuu->type = 0;
976 }
977 if (uuu->descr) {
978 free((void*) uuu->descr);
979 uuu->descr = 0;
980 }
981 if (!(net_info = ConnNetInfo_Clone(uuu->net_info))) {
982 status = eIO_Unknown;
983 break;
984 }
985
986 c = s_Open(uuu, timeout, info, net_info, &status);
987 stateless = net_info->stateless;
988
989 ConnNetInfo_Destroy(net_info);
990
991 if (!c) {
992 if (status == eIO_Success)
993 status = eIO_Closed;
994 continue;
995 }
996
997 /* Setup the new connector on a temporary meta-connector... */
998 memset(&uuu->meta, 0, sizeof(uuu->meta));
999 if ((status = METACONN_Insert(&uuu->meta, c)) != eIO_Success) {
1000 x_DestroyConnector(c);
1001 continue;
1002 }
1003 /* ...then link it in using current connection's meta */
1004 assert(c->meta == &uuu->meta);
1005 c->next = meta->list;
1006 meta->list = c;
1007
1008 if (!uuu->descr && uuu->meta.descr)
1009 CONN_SET_METHOD(meta, descr, uuu->meta.descr, uuu->meta.c_descr);
1010 CONN_SET_METHOD (meta, wait, uuu->meta.wait, uuu->meta.c_wait);
1011 CONN_SET_METHOD (meta, write, uuu->meta.write, uuu->meta.c_write);
1012 CONN_SET_METHOD (meta, flush, uuu->meta.flush, uuu->meta.c_flush);
1013 CONN_SET_METHOD (meta, read, uuu->meta.read, uuu->meta.c_read);
1014 CONN_SET_METHOD (meta, status, uuu->meta.status,uuu->meta.c_status);
1015
1016 if (uuu->meta.get_type) {
1017 const char* temp;
1018 if ((temp = uuu->meta.get_type(uuu->meta.c_get_type)) != 0) {
1019 size_t slen = strlen(uuu->service);
1020 size_t tlen = strlen(temp);
1021 char* type = (char*) malloc(slen + tlen + 2);
1022 if (type) {
1023 memcpy(type, uuu->service, slen);
1024 type[slen++] = '/';
1025 memcpy(type + slen, temp, tlen);
1026 type[slen + tlen] = '\0';
1027 uuu->type = type;
1028 }
1029 }
1030 }
1031
1032 if (!uuu->meta.open) {
1033 s_Close(connector, timeout, 0/*retain*/);
1034 status = eIO_NotSupported;
1035 continue;
1036 }
1037
1038 status = uuu->meta.open(uuu->meta.c_open, timeout);
1039 if (status == eIO_Success)
1040 break;
1041
1042 if (!stateless && (!info || info->type == fSERV_Firewall)) {
1043 static const char kFWDLink[] = CONN_FWD_LINK;
1044 CORE_LOGF_X(6, eLOG_Error,
1045 ("[%s] %s connection failure (%s) usually"
1046 " indicates possible firewall configuration"
1047 " problems; please consult <%s>", uuu->service,
1048 !info ? "Firewall" : "Stateful relay",
1049 IO_StatusStr(status), kFWDLink));
1050 }
1051
1052 s_Close(connector, timeout, 0/*retain*/);
1053 }
1054
1055 uuu->status = status;
1056 return status;
1057 }
1058
1059
1060 /*ARGSUSED*/
s_VT_Status(CONNECTOR connector,EIO_Event unused)1061 static EIO_Status s_VT_Status(CONNECTOR connector, EIO_Event unused)
1062 {
1063 return ((SServiceConnector*) connector->handle)->status;
1064 }
1065
1066
s_VT_Close(CONNECTOR connector,const STimeout * timeout)1067 static EIO_Status s_VT_Close(CONNECTOR connector, const STimeout* timeout)
1068 {
1069 return s_Close(connector, timeout, 1/*cleanup*/);
1070 }
1071
1072
s_Setup(CONNECTOR connector)1073 static void s_Setup(CONNECTOR connector)
1074 {
1075 SServiceConnector* uuu = (SServiceConnector*) connector->handle;
1076 SMetaConnector* meta = connector->meta;
1077
1078 /* initialize virtual table */
1079 CONN_SET_METHOD(meta, get_type, s_VT_GetType, connector);
1080 CONN_SET_METHOD(meta, open, s_VT_Open, connector);
1081 CONN_SET_METHOD(meta, close, s_VT_Close, connector);
1082 CONN_SET_DEFAULT_TIMEOUT(meta, uuu->net_info->timeout);
1083 /* reset everything else */
1084 s_Reset(meta, connector);
1085 }
1086
1087
s_Destroy(CONNECTOR connector)1088 static void s_Destroy(CONNECTOR connector)
1089 {
1090 SServiceConnector* uuu = (SServiceConnector*) connector->handle;
1091 connector->handle = 0;
1092
1093 if (uuu->extra.cleanup)
1094 uuu->extra.cleanup(uuu->extra.data);
1095 s_CloseDispatcher(uuu);
1096 s_Cleanup(uuu);
1097 ConnNetInfo_Destroy(uuu->net_info);
1098 free(uuu);
1099 free(connector);
1100 }
1101
1102
1103 /***********************************************************************
1104 * EXTERNAL -- the connector's "constructor"
1105 ***********************************************************************/
1106
SERVICE_CreateConnectorEx(const char * service,TSERV_Type types,const SConnNetInfo * net_info,const SSERVICE_Extra * extra)1107 extern CONNECTOR SERVICE_CreateConnectorEx
1108 (const char* service,
1109 TSERV_Type types,
1110 const SConnNetInfo* net_info,
1111 const SSERVICE_Extra* extra)
1112 {
1113 char* x_service;
1114 CONNECTOR ccc;
1115 size_t len;
1116 SServiceConnector* xxx;
1117
1118 if (!service || !*service || !(x_service = SERV_ServiceName(service)))
1119 return 0;
1120
1121 if (!(ccc = (SConnector*) malloc(sizeof(SConnector)))) {
1122 free(x_service);
1123 return 0;
1124 }
1125 len = strlen(service);
1126 if (!(xxx = (SServiceConnector*) calloc(1, sizeof(*xxx) + len))) {
1127 free(x_service);
1128 free(ccc);
1129 return 0;
1130 }
1131
1132 /* initialize connector structures */
1133 ccc->handle = xxx;
1134 ccc->next = 0;
1135 ccc->meta = 0;
1136 ccc->setup = s_Setup;
1137 ccc->destroy = s_Destroy;
1138
1139 xxx->types = types;
1140 xxx->net_info = (net_info
1141 ? ConnNetInfo_Clone(net_info)
1142 : ConnNetInfo_Create(service));
1143
1144 if (!ConnNetInfo_SetupStandardArgs(xxx->net_info, x_service)) {
1145 free(x_service);
1146 s_Destroy(ccc);
1147 return 0;
1148 }
1149 /* NB: zero'ed block, no need to copy trailing '\0' */
1150 memcpy((char*) xxx->service, service, len);
1151 free(x_service);
1152
1153 /* now get ready for first probe dispatching */
1154 if ( types & fSERV_Stateless )
1155 xxx->net_info->stateless = 1/*true*/;
1156 if ((types & fSERV_Firewall) && !xxx->net_info->firewall)
1157 xxx->net_info->firewall = eFWMode_Adaptive;
1158 if (xxx->net_info->max_try < 1)
1159 xxx->net_info->max_try = 1;
1160 if (!s_OpenDispatcher(xxx)) {
1161 s_Destroy(ccc);
1162 return 0;
1163 }
1164 assert(xxx->iter);
1165
1166 /* finally, store all callback extras */
1167 if (extra)
1168 memcpy(&xxx->extra, extra, sizeof(xxx->extra));
1169
1170 /* done */
1171 return ccc;
1172 }
1173