1 /* $Id: ncbi_conn_stream.cpp 624530 2021-02-01 13:22:09Z ivanov $
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 * Authors: Anton Lavrentiev, Denis Vakatov
27 *
28 * File Description:
29 * CONN-based C++ streams
30 *
31 * See file <connect/ncbi_conn_stream.hpp> for more detailed information.
32 *
33 */
34
35 #include <ncbi_pch.hpp>
36 #include "ncbi_ansi_ext.h"
37 #include "ncbi_conn_streambuf.hpp"
38 #include "ncbi_servicep.h"
39 #include "ncbi_socketp.h"
40 #include <connect/ncbi_conn_exception.hpp>
41 #include <connect/ncbi_conn_stream.hpp>
42 #include <connect/ncbi_file_connector.h>
43 #include <connect/ncbi_util.h>
44 #include <stdlib.h>
45
46
47 BEGIN_NCBI_SCOPE
48
49
CConn_IOStream(const TConnector & connector,const STimeout * timeout,size_t buf_size,TConn_Flags flgs,CT_CHAR_TYPE * ptr,size_t size)50 CConn_IOStream::CConn_IOStream(const TConnector& connector,
51 const STimeout* timeout,
52 size_t buf_size, TConn_Flags flgs,
53 CT_CHAR_TYPE* ptr, size_t size)
54 : CNcbiIostream(0), m_CSb(new CConn_Streambuf(connector.first,
55 connector.second,
56 timeout, buf_size, flgs,
57 ptr, size))
58 {
59 init(Status() != eIO_Success
60 ? 0 // according to the standard (27.4.4.1.3), badbit is set here
61 : m_CSb);
62 }
63
64
CConn_IOStream(CONN conn,bool close,const STimeout * timeout,size_t buf_size,TConn_Flags flgs,CT_CHAR_TYPE * ptr,size_t size)65 CConn_IOStream::CConn_IOStream(CONN conn, bool close,
66 const STimeout* timeout,
67 size_t buf_size, TConn_Flags flgs,
68 CT_CHAR_TYPE* ptr, size_t size)
69 : CNcbiIostream(0), m_CSb(new CConn_Streambuf(conn, close,
70 timeout, buf_size, flgs,
71 ptr, size))
72 {
73 init(Status() != eIO_Success
74 ? 0 // according to the standard (27.4.4.1.3), badbit is set here
75 : m_CSb);
76 }
77
78
~CConn_IOStream()79 CConn_IOStream::~CConn_IOStream()
80 {
81 x_Destroy();
82 }
83
84
85 #define GET_CONN(sb) ((sb) ? (sb)->GetCONN() : 0)
86
87
GetCONN(void) const88 CONN CConn_IOStream::GetCONN(void) const
89 {
90 return GET_CONN(m_CSb);
91 }
92
93
GetType(void) const94 string CConn_IOStream::GetType(void) const
95 {
96 CONN conn = GET_CONN(m_CSb);
97 const char* type = conn ? CONN_GetType(conn) : 0;
98 return type ? type : kEmptyStr;
99 }
100
101
GetDescription(void) const102 string CConn_IOStream::GetDescription(void) const
103 {
104 CONN conn = GET_CONN(m_CSb);
105 char* text = conn ? CONN_Description(conn) : 0;
106 string retval(text ? text : kEmptyStr);
107 if (text)
108 free(text);
109 return retval;
110 }
111
112
SetTimeout(EIO_Event direction,const STimeout * timeout) const113 EIO_Status CConn_IOStream::SetTimeout(EIO_Event direction,
114 const STimeout* timeout) const
115 {
116 CONN conn = GET_CONN(m_CSb);
117 return conn ? CONN_SetTimeout(conn, direction, timeout) : eIO_NotSupported;
118 }
119
120
GetTimeout(EIO_Event direction) const121 const STimeout* CConn_IOStream::GetTimeout(EIO_Event direction) const
122 {
123 CONN conn = GET_CONN(m_CSb);
124 return conn ? CONN_GetTimeout(conn, direction) : kDefaultTimeout;
125 }
126
127
Status(EIO_Event dir) const128 EIO_Status CConn_IOStream::Status(EIO_Event dir) const
129 {
130 return m_CSb ? m_CSb->Status(dir) : eIO_NotSupported;
131 }
132
133
Fetch(const STimeout * timeout)134 EIO_Status CConn_IOStream::Fetch(const STimeout* timeout)
135 {
136 EIO_Status status;
137 CONN conn = GET_CONN(m_CSb);
138 if (!conn) {
139 setstate(NcbiBadbit);
140 status = eIO_NotSupported;
141 } else
142 status = m_CSb->Fetch(timeout);
143 return status;
144 }
145
146
Pushback(const CT_CHAR_TYPE * data,streamsize size)147 EIO_Status CConn_IOStream::Pushback(const CT_CHAR_TYPE* data, streamsize size)
148 {
149 EIO_Status status = m_CSb ? m_CSb->Pushback(data, size) : eIO_NotSupported;
150 if (status != eIO_Success)
151 clear(NcbiBadbit);
152 return status;
153 }
154
155
GetSOCK(void)156 SOCK CConn_IOStream::GetSOCK(void)
157 {
158 SOCK sock;
159 CONN conn = GET_CONN(m_CSb);
160 if (!conn || CONN_GetSOCK(conn, &sock) != eIO_Success)
161 sock = 0;
162 return sock;
163 }
164
165
Close(void)166 EIO_Status CConn_IOStream::Close(void)
167 {
168 if (!m_CSb)
169 return eIO_Closed;
170 EIO_Status status = m_CSb->Close();
171 if (status != eIO_Success && status != eIO_Closed)
172 clear(NcbiBadbit);
173 return status;
174 }
175
176
x_Destroy(void)177 void CConn_IOStream::x_Destroy(void)
178 {
179 CConn_Streambuf* sb = m_CSb;
180 m_CSb = 0;
181 delete sb;
182 }
183
184
SetCanceledCallback(const ICanceled * canceled)185 EIO_Status CConn_IOStream::SetCanceledCallback(const ICanceled* canceled)
186 {
187 CONN conn = GET_CONN(m_CSb);
188 if (!conn)
189 return eIO_NotSupported;
190
191 bool isset = m_Canceled.NotNull() ? 1 : 0;
192
193 if (canceled) {
194 SCONN_Callback cb;
195 m_Canceled = canceled;
196 memset(&cb, 0, sizeof(cb));
197 cb.func = (FCONN_Callback) x_IsCanceled;
198 cb.data = this;
199 CONN_SetCallback(conn, eCONN_OnOpen, &cb, isset ? 0 : &m_CB[0]);
200 CONN_SetCallback(conn, eCONN_OnRead, &cb, isset ? 0 : &m_CB[1]);
201 CONN_SetCallback(conn, eCONN_OnWrite, &cb, isset ? 0 : &m_CB[2]);
202 CONN_SetCallback(conn, eCONN_OnFlush, &cb, isset ? 0 : &m_CB[3]);
203 } else if (isset) {
204 CONN_SetCallback(conn, eCONN_OnFlush, &m_CB[3], 0);
205 CONN_SetCallback(conn, eCONN_OnWrite, &m_CB[2], 0);
206 CONN_SetCallback(conn, eCONN_OnRead, &m_CB[1], 0);
207 CONN_SetCallback(conn, eCONN_OnOpen, &m_CB[0], 0);
208 m_Canceled = 0;
209 }
210
211 return eIO_Success;
212 }
213
214
x_IsCanceled(CONN conn,TCONN_Callback type,void * data)215 EIO_Status CConn_IOStream::x_IsCanceled(CONN conn,
216 TCONN_Callback type,
217 void* data)
218 {
219 _ASSERT(conn && data);
220 CConn_IOStream* io = reinterpret_cast<CConn_IOStream*>(data);
221 if (/* io && */ io->m_Canceled.NotNull() && io->m_Canceled->IsCanceled())
222 return eIO_Interrupt;
223 int n = (int) type & (int) eIO_ReadWrite;
224 _ASSERT(0 <= n && (size_t) n < sizeof(io->m_CB) / sizeof(io->m_CB[0]));
225 _ASSERT((n == 0 && type == eCONN_OnOpen) ||
226 (n == 1 && type == eCONN_OnRead) ||
227 (n == 2 && type == eCONN_OnWrite) ||
228 (n == 3 && type == eCONN_OnFlush));
229 return io->m_CB[n].func
230 ? io->m_CB[n].func(conn, type, io->m_CB[n].data)
231 : eIO_Success;
232
233 }
234
235
CConn_SocketStream(const string & host,unsigned short port,unsigned short max_try,const STimeout * timeout,size_t buf_size)236 CConn_SocketStream::CConn_SocketStream(const string& host,
237 unsigned short port,
238 unsigned short max_try,
239 const STimeout* timeout,
240 size_t buf_size)
241 : CConn_IOStream(TConnector(SOCK_CreateConnector(host.c_str(),
242 port,
243 max_try)),
244 timeout, buf_size)
245 {
246 return;
247 }
248
249
CConn_SocketStream(const string & host,unsigned short port,const void * data,size_t size,TSOCK_Flags flgs,unsigned short max_try,const STimeout * timeout,size_t buf_size)250 CConn_SocketStream::CConn_SocketStream(const string& host,
251 unsigned short port,
252 const void* data,
253 size_t size,
254 TSOCK_Flags flgs,
255 unsigned short max_try,
256 const STimeout* timeout,
257 size_t buf_size)
258 : CConn_IOStream(TConnector(SOCK_CreateConnectorEx(host.c_str(),
259 port,
260 max_try,
261 data,
262 size,
263 flgs)),
264 timeout, buf_size)
265 {
266 return;
267 }
268
269
CConn_SocketStream(SOCK sock,EOwnership if_to_own,const STimeout * timeout,size_t buf_size)270 CConn_SocketStream::CConn_SocketStream(SOCK sock,
271 EOwnership if_to_own,
272 const STimeout* timeout,
273 size_t buf_size)
274 : CConn_IOStream(TConnector(SOCK_CreateConnectorOnTop(sock,
275 if_to_own
276 != eNoOwnership)),
277 timeout, buf_size)
278 {
279 return;
280 }
281
282
s_GrabSOCK(CSocket & socket)283 static SOCK s_GrabSOCK(CSocket& socket)
284 {
285 SOCK sock = socket.GetSOCK();
286 if (!sock) {
287 NCBI_THROW(CIO_Exception, eInvalidArg,
288 "CConn_SocketStream::CConn_SocketStream(): "
289 " Socket may not be empty");
290 }
291 if (socket.SetOwnership(eNoOwnership) == eNoOwnership) {
292 NCBI_THROW(CIO_Exception, eInvalidArg,
293 "CConn_SocketStream::CConn_SocketStream(): "
294 " Socket must be owned");
295 }
296 socket.Reset(0/*empty*/,
297 eNoOwnership/*irrelevant*/,
298 eCopyTimeoutsFromSOCK/*irrelevant*/);
299 return sock;
300 }
301
302
CConn_SocketStream(CSocket & socket,const STimeout * timeout,size_t buf_size)303 CConn_SocketStream::CConn_SocketStream(CSocket& socket,
304 const STimeout* timeout,
305 size_t buf_size)
306 : CConn_IOStream(TConnector(SOCK_CreateConnectorOnTop(s_GrabSOCK(socket),
307 1/*own*/)),
308 timeout, buf_size)
309 {
310 return;
311 }
312
313
314 template<>
315 struct Deleter<SConnNetInfo>
316 {
DeleteDeleter317 static void Delete(SConnNetInfo* net_info)
318 { ConnNetInfo_Destroy(net_info); }
319 };
320
321
322 static CConn_IOStream::TConnector
s_SocketConnectorBuilder(const SConnNetInfo * net_info,const STimeout * timeout,const void * data,size_t size,TSOCK_Flags flgs)323 s_SocketConnectorBuilder(const SConnNetInfo* net_info,
324 const STimeout* timeout,
325 const void* data,
326 size_t size,
327 TSOCK_Flags flgs)
328 {
329 EIO_Status status = eIO_Success;
330 bool proxy = false;
331 SOCK sock = 0;
332
333 _ASSERT(net_info);
334 if ((flgs & (fSOCK_LogOn | fSOCK_LogDefault)) == fSOCK_LogDefault
335 && net_info->debug_printout == eDebugPrintout_Data) {
336 flgs &= ~fSOCK_LogDefault;
337 flgs |= fSOCK_LogOn;
338 }
339 if (net_info->http_proxy_host[0] && net_info->http_proxy_port) {
340 status = HTTP_CreateTunnel(net_info, fHTTP_NoAutoRetry, &sock);
341 _ASSERT(!sock ^ !(status != eIO_Success));
342 if (status == eIO_Success
343 && (size || (flgs & ~(fSOCK_LogOn | fSOCK_LogDefault)))) {
344 SSOCK_Init init;
345 memset(&init, 0, sizeof(init));
346 init.data = data;
347 init.size = size;
348 init.host = net_info->host;
349 init.cred = net_info->credentials;
350 SOCK s;
351 status = SOCK_CreateOnTopInternal(sock, 0, &s, &init, flgs);
352 _ASSERT(!s ^ !(status != eIO_Success));
353 SOCK_Destroy(sock);
354 sock = s;
355 }
356 proxy = true;
357 }
358 if (!sock && (!proxy || net_info->http_proxy_leak)) {
359 if (timeout == kDefaultTimeout)
360 timeout = net_info->timeout;
361 if (!proxy && net_info->debug_printout) {
362 AutoPtr<SConnNetInfo> x_net_info(ConnNetInfo_Clone(net_info));
363 if (x_net_info.get()) {
364 // manual cleanup of most fields req'd
365 x_net_info->scheme = eURL_Unspec;
366 x_net_info->req_method = eReqMethod_Any;
367 x_net_info->external = 0;
368 x_net_info->firewall = 0;
369 x_net_info->stateless = 0;
370 x_net_info->lb_disable = 0;
371 x_net_info->http_version = 0;
372 x_net_info->http_push_auth = 0;
373 x_net_info->http_proxy_leak = 0;
374 x_net_info->user[0] = '\0';
375 x_net_info->pass[0] = '\0';
376 x_net_info->path[0] = '\0';
377 x_net_info->http_proxy_host[0] = '\0';
378 x_net_info->http_proxy_port = 0;
379 x_net_info->http_proxy_user[0] = '\0';
380 x_net_info->http_proxy_pass[0] = '\0';
381 ConnNetInfo_SetUserHeader(x_net_info.get(), 0);
382 if (x_net_info->http_referer) {
383 free((void*) x_net_info->http_referer);
384 x_net_info->http_referer = 0;
385 }
386 x_net_info->timeout = timeout;
387 }
388 ConnNetInfo_Log(x_net_info.get(), eLOG_Note, CORE_GetLOG());
389 }
390 SSOCK_Init init;
391 memset(&init, 0, sizeof(init));
392 init.data = data;
393 init.size = size;
394 init.host = net_info->host;
395 init.cred = net_info->credentials;
396 status = SOCK_CreateInternal(net_info->host, net_info->port, timeout,
397 &sock, &init, flgs);
398 _ASSERT(!sock ^ !(status != eIO_Success));
399 }
400 string hostport(net_info->host);
401 hostport += ':';
402 hostport += NStr::UIntToString(net_info->port);
403 CONNECTOR c = SOCK_CreateConnectorOnTopEx(sock, 1/*own*/,hostport.c_str());
404 if (!c) {
405 SOCK_Abort(sock);
406 SOCK_Close(sock);
407 status = eIO_Unknown;
408 }
409 return CConn_IOStream::TConnector(c, status);
410 }
411
412
CConn_SocketStream(const SConnNetInfo & net_info,const void * data,size_t size,TSOCK_Flags flgs,const STimeout * timeout,size_t buf_size)413 CConn_SocketStream::CConn_SocketStream(const SConnNetInfo& net_info,
414 const void* data,
415 size_t size,
416 TSOCK_Flags flgs,
417 const STimeout* timeout,
418 size_t buf_size)
419 : CConn_IOStream(s_SocketConnectorBuilder(&net_info, timeout,
420 data, size, flgs),
421 timeout, buf_size)
422 {
423 return;
424 }
425
426
427 // NB: Must never be upcalled (directly or indirectly) from any stream ctor!
Parse(const char * header)428 EHTTP_HeaderParse SHTTP_StatusData::Parse(const char* header)
429 {
430 int c, n;
431 m_Code = 0;
432 m_Text.clear();
433 m_Header = header;
434 if (sscanf(header, "%*s %u%n", &c, &n) < 1)
435 return eHTTP_HeaderError;
436 const char* str = m_Header.c_str() + n;
437 str += strspn(str, " \t");
438 const char* eol = strchr(str, '\n');
439 if (!eol)
440 eol = str + strlen(str);
441 while (eol > str) {
442 if (!isspace((unsigned char) eol[-1]))
443 break;
444 --eol;
445 }
446 m_Code = c;
447 m_Text.assign(str, (size_t)(eol - str));
448 return eHTTP_HeaderSuccess;
449 }
450
451
452 typedef AutoPtr< char, CDeleter<char> > TTempCharPtr;
453
454
455 static CConn_IOStream::TConnector
s_HttpConnectorBuilder(const SConnNetInfo * net_info,EReqMethod method,const char * url,const char * host,unsigned short port,const char * path,const char * args,const char * user_header,void * x_data,FHTTP_Adjust x_adjust,FHTTP_Cleanup x_cleanup,FHTTP_ParseHeader x_parse_header,THTTP_Flags flgs,const STimeout * timeout,void ** user_data_ptr,FHTTP_Cleanup * user_cleanup_ptr,void * user_data=0,FHTTP_Cleanup user_cleanup=0)456 s_HttpConnectorBuilder(const SConnNetInfo* net_info,
457 EReqMethod method,
458 const char* url,
459 const char* host,
460 unsigned short port,
461 const char* path,
462 const char* args,
463 const char* user_header,
464 void* x_data,
465 FHTTP_Adjust x_adjust,
466 FHTTP_Cleanup x_cleanup,
467 FHTTP_ParseHeader x_parse_header,
468 THTTP_Flags flgs,
469 const STimeout* timeout,
470 void** user_data_ptr,
471 FHTTP_Cleanup* user_cleanup_ptr,
472 void* user_data = 0,
473 FHTTP_Cleanup user_cleanup = 0)
474 {
475 EReqMethod x_req_method;
476 AutoPtr<SConnNetInfo> x_net_info(net_info
477 ? ConnNetInfo_Clone(net_info)
478 : ConnNetInfo_CreateInternal(0));
479 if (!x_net_info.get()) {
480 NCBI_THROW(CIO_Exception, eUnknown,
481 "CConn_HttpStream::CConn_HttpStream(): "
482 " Out of memory");
483 }
484 x_req_method = (EReqMethod)(method & ~eReqMethod_v1);
485 if (x_req_method == eReqMethod_Connect) {
486 NCBI_THROW(CIO_Exception, eInvalidArg,
487 "CConn_HttpStream::CConn_HttpStream(): "
488 " Bad request method (CONNECT)");
489 }
490 if (x_req_method)
491 x_net_info->req_method = method;
492 else if (method/*ANY/1.1*/)
493 x_net_info->http_version = 1;
494 if (url && !ConnNetInfo_ParseURL(x_net_info.get(), url)) {
495 NCBI_THROW(CIO_Exception, eInvalidArg,
496 "CConn_HttpStream::CConn_HttpStream(): "
497 " Bad URL \"" + string(url) + '"');
498 }
499 if (host) {
500 size_t len;
501 if ((len = *host ? strlen(host) : 0) >= sizeof(x_net_info->host)) {
502 NCBI_THROW(CIO_Exception, eInvalidArg,
503 "CConn_HttpStream::CConn_HttpStream(): "
504 " Host too long \"" + string(host) + '"');
505 }
506 memcpy(x_net_info->host, host, ++len);
507 }
508 if (port)
509 x_net_info->port = port;
510 if (path && !ConnNetInfo_SetPath(x_net_info.get(), path)) {
511 NCBI_THROW(CIO_Exception, eInvalidArg,
512 "CConn_HttpStream::CConn_HttpStream(): "
513 " Path too long \"" + string(path) + '"');
514 }
515 if (args && !ConnNetInfo_SetArgs(x_net_info.get(), args)) {
516 NCBI_THROW(CIO_Exception, eInvalidArg,
517 "CConn_HttpStream::CConn_HttpStream(): "
518 " Args too long \"" + string(args) + '"');
519 }
520 if (user_header && *user_header
521 && !ConnNetInfo_OverrideUserHeader(x_net_info.get(), user_header)) {
522 int x_dynamic = 0;
523 const char* x_message = NcbiMessagePlusError(&x_dynamic,
524 "Cannot set user header",
525 errno, 0);
526 TTempCharPtr msg_ptr(const_cast<char*> (x_message),
527 x_dynamic ? eTakeOwnership : eNoOwnership);
528 NCBI_THROW(CIO_Exception, eUnknown,
529 "CConn_HttpStream::CConn_HttpStream(): "
530 " " + string(msg_ptr.get()));
531 }
532 if (timeout != kDefaultTimeout)
533 x_net_info->timeout = timeout;
534 // NB: need these two inited now in case of early CONNECTOR->destroy() call
535 *user_data_ptr = user_data;
536 *user_cleanup_ptr = user_cleanup;
537 CONNECTOR c = HTTP_CreateConnectorEx(x_net_info.get(),
538 flgs,
539 x_parse_header,
540 x_data,
541 x_adjust,
542 x_cleanup);
543 return CConn_IOStream::TConnector(c);
544 }
545
546
CConn_HttpStream(const string & host,const string & path,const string & args,const string & user_header,unsigned short port,THTTP_Flags flgs,const STimeout * timeout,size_t buf_size)547 CConn_HttpStream::CConn_HttpStream(const string& host,
548 const string& path,
549 const string& args,
550 const string& user_header,
551 unsigned short port,
552 THTTP_Flags flgs,
553 const STimeout* timeout,
554 size_t buf_size)
555 : CConn_IOStream(s_HttpConnectorBuilder(0,
556 eReqMethod_Any,
557 0,
558 host.c_str(),
559 port,
560 path.c_str(),
561 args.c_str(),
562 user_header.c_str(),
563 this,
564 x_Adjust,
565 0/*x_Cleanup*/,
566 x_ParseHeader,
567 flgs,
568 timeout,
569 &m_UserData,
570 &m_UserCleanup),
571 timeout, buf_size),
572 m_UserAdjust(0), m_UserParseHeader(0)
573 {
574 return;
575 }
576
577
CConn_HttpStream(const string & url,THTTP_Flags flgs,const STimeout * timeout,size_t buf_size)578 CConn_HttpStream::CConn_HttpStream(const string& url,
579 THTTP_Flags flgs,
580 const STimeout* timeout,
581 size_t buf_size)
582 : CConn_IOStream(s_HttpConnectorBuilder(0,
583 eReqMethod_Any,
584 url.c_str(),
585 0,
586 0,
587 0,
588 0,
589 0,
590 this,
591 x_Adjust,
592 0/*x_Cleanup*/,
593 x_ParseHeader,
594 flgs,
595 timeout,
596 &m_UserData,
597 &m_UserCleanup),
598 timeout, buf_size),
599 m_UserAdjust(0), m_UserParseHeader(0)
600 {
601 return;
602 }
603
604
CConn_HttpStream(const string & url,EReqMethod method,const string & user_header,THTTP_Flags flgs,const STimeout * timeout,size_t buf_size)605 CConn_HttpStream::CConn_HttpStream(const string& url,
606 EReqMethod method,
607 const string& user_header,
608 THTTP_Flags flgs,
609 const STimeout* timeout,
610 size_t buf_size)
611 : CConn_IOStream(s_HttpConnectorBuilder(0,
612 method,
613 url.c_str(),
614 0,
615 0,
616 0,
617 0,
618 user_header.c_str(),
619 this,
620 x_Adjust,
621 0/*x_Cleanup*/,
622 x_ParseHeader,
623 flgs,
624 timeout,
625 &m_UserData,
626 &m_UserCleanup),
627 timeout, buf_size),
628 m_UserAdjust(0), m_UserParseHeader(0)
629 {
630 return;
631 }
632
633
CConn_HttpStream(const string & url,const SConnNetInfo * net_info,const string & user_header,FHTTP_ParseHeader parse_header,void * user_data,FHTTP_Adjust adjust,FHTTP_Cleanup cleanup,THTTP_Flags flgs,const STimeout * timeout,size_t buf_size)634 CConn_HttpStream::CConn_HttpStream(const string& url,
635 const SConnNetInfo* net_info,
636 const string& user_header,
637 FHTTP_ParseHeader parse_header,
638 void* user_data,
639 FHTTP_Adjust adjust,
640 FHTTP_Cleanup cleanup,
641 THTTP_Flags flgs,
642 const STimeout* timeout,
643 size_t buf_size)
644 : CConn_IOStream(s_HttpConnectorBuilder(net_info,
645 eReqMethod_Any,
646 url.c_str(),
647 0,
648 0,
649 0,
650 0,
651 user_header.c_str(),
652 this,
653 x_Adjust,
654 cleanup ? x_Cleanup : 0,
655 x_ParseHeader,
656 flgs,
657 timeout,
658 &m_UserData,
659 &m_UserCleanup,
660 user_data,
661 cleanup),
662 timeout, buf_size),
663 m_UserAdjust(adjust), m_UserParseHeader(parse_header)
664 {
665 return;
666 }
667
668
CConn_HttpStream(const SConnNetInfo * net_info,const string & user_header,FHTTP_ParseHeader parse_header,void * user_data,FHTTP_Adjust adjust,FHTTP_Cleanup cleanup,THTTP_Flags flgs,const STimeout * timeout,size_t buf_size)669 CConn_HttpStream::CConn_HttpStream(const SConnNetInfo* net_info,
670 const string& user_header,
671 FHTTP_ParseHeader parse_header,
672 void* user_data,
673 FHTTP_Adjust adjust,
674 FHTTP_Cleanup cleanup,
675 THTTP_Flags flgs,
676 const STimeout* timeout,
677 size_t buf_size)
678 : CConn_IOStream(s_HttpConnectorBuilder(net_info,
679 eReqMethod_Any,
680 0,
681 0,
682 0,
683 0,
684 0,
685 user_header.c_str(),
686 this,
687 x_Adjust,
688 cleanup ? x_Cleanup : 0,
689 x_ParseHeader,
690 flgs,
691 timeout,
692 &m_UserData,
693 &m_UserCleanup,
694 user_data,
695 cleanup),
696 timeout, buf_size),
697 m_UserAdjust(adjust), m_UserParseHeader(parse_header)
698 {
699 return;
700 }
701
702
~CConn_HttpStream()703 CConn_HttpStream::~CConn_HttpStream()
704 {
705 // Explicitly destroy so that the callbacks are not called out of context
706 x_Destroy();
707 }
708
709
710 // WARNING: must not be called from CConn_HttpStream ctor (directly or not)!
x_ParseHeader(const char * header,void * data,int code)711 EHTTP_HeaderParse CConn_HttpStream::x_ParseHeader(const char* header,
712 void* data,
713 int code)
714 {
715 CConn_HttpStream* http = reinterpret_cast<CConn_HttpStream*>(data);
716 EHTTP_HeaderParse rv = http->m_StatusData.Parse(header);
717 if (rv != eHTTP_HeaderSuccess)
718 return rv;
719 _ASSERT(!code || code == http->m_StatusData.m_Code);
720 return http->m_UserParseHeader
721 ? http->m_UserParseHeader(header, http->m_UserData, code)
722 : eHTTP_HeaderSuccess;
723 }
724
725
x_Adjust(SConnNetInfo * net_info,void * data,unsigned int count)726 int CConn_HttpStream::x_Adjust(SConnNetInfo* net_info,
727 void* data,
728 unsigned int count)
729 {
730 int retval;
731 bool modified;
732 CConn_HttpStream* http = reinterpret_cast<CConn_HttpStream*>(data);
733 if (count == (unsigned int)(-1) && !http->m_URL.empty()) {
734 http->m_StatusData.Clear();
735 if (!ConnNetInfo_ParseURL(net_info, http->m_URL.c_str()))
736 return 0/*failure*/;
737 http->m_URL.erase();
738 modified = true;
739 } else
740 modified = false;
741 if (http->m_UserAdjust) {
742 if (!(retval = http->m_UserAdjust(net_info, http->m_UserData, count)))
743 return 0/*failure*/;
744 if (retval < 0 && modified)
745 retval = 1;
746 } else
747 retval = modified ? 1 : -1;
748 return retval/*success*/;
749 }
750
751
x_Cleanup(void * data)752 void CConn_HttpStream::x_Cleanup(void* data)
753 {
754 CConn_HttpStream* http = reinterpret_cast<CConn_HttpStream*>(data);
755 http->m_UserCleanup(http->m_UserData);
756 }
757
758
759 static CConn_IOStream::TConnector
s_ServiceConnectorBuilder(const char * service,TSERV_Type types,const SConnNetInfo * net_info,const char * user_header,const SSERVICE_Extra * extra,CConn_ServiceStream::SSERVICE_CBData * cbdata,FSERVICE_Reset x_reset,FHTTP_Adjust x_adjust,FSERVICE_Cleanup x_cleanup,FHTTP_ParseHeader x_parse_header,FSERVICE_GetNextInfo x_get_next_info,const STimeout * timeout)760 s_ServiceConnectorBuilder(const char* service,
761 TSERV_Type types,
762 const SConnNetInfo* net_info,
763 const char* user_header,
764 const SSERVICE_Extra* extra,
765 CConn_ServiceStream::SSERVICE_CBData*cbdata,
766 FSERVICE_Reset x_reset,
767 FHTTP_Adjust x_adjust,
768 FSERVICE_Cleanup x_cleanup,
769 FHTTP_ParseHeader x_parse_header,
770 FSERVICE_GetNextInfo x_get_next_info,
771 const STimeout* timeout)
772 {
773 AutoPtr<SConnNetInfo> x_net_info(net_info
774 ? ConnNetInfo_Clone(net_info)
775 : ConnNetInfo_Create(service));
776 if (!x_net_info.get()) {
777 NCBI_THROW(CIO_Exception, eUnknown,
778 "CConn_ServiceStream::CConn_ServiceStream(): "
779 " Out of memory");
780 }
781 if (user_header && *user_header
782 && !ConnNetInfo_OverrideUserHeader(x_net_info.get(), user_header)) {
783 int x_dynamic = 0;
784 const char* x_message = NcbiMessagePlusError(&x_dynamic,
785 "Cannot set user header",
786 errno, 0);
787 TTempCharPtr msg_ptr(const_cast<char*> (x_message),
788 x_dynamic ? eTakeOwnership : eNoOwnership);
789 NCBI_THROW(CIO_Exception, eUnknown,
790 "CConn_ServiceStream::CConn_ServiceStream(): "
791 " " + string(msg_ptr.get()));
792 }
793 if (timeout != kDefaultTimeout)
794 x_net_info->timeout = timeout;
795 if (extra)
796 memcpy(&cbdata->extra, extra, sizeof(cbdata->extra));
797 else
798 memset(&cbdata->extra, 0, sizeof(cbdata->extra));
799 _ASSERT(!x_reset || (extra && extra->reset));
800 _ASSERT(!x_adjust || (extra && extra->adjust));
801 _ASSERT(!x_cleanup || (extra && extra->cleanup));
802 _ASSERT(!x_get_next_info || (extra && extra->get_next_info));
803 SSERVICE_Extra x_extra;
804 memset(&x_extra, 0, sizeof(x_extra));
805 x_extra.data = cbdata;
806 x_extra.reset = x_reset;
807 x_extra.adjust = x_adjust;
808 x_extra.cleanup = x_cleanup;
809 x_extra.parse_header = x_parse_header;
810 x_extra.get_next_info = x_get_next_info;
811 x_extra.flags = extra ? extra->flags : 0;
812 CONNECTOR c = SERVICE_CreateConnectorEx(service,
813 types,
814 x_net_info.get(),
815 &x_extra);
816 return CConn_IOStream::TConnector(c);
817 }
818
819
CConn_ServiceStream(const string & service,TSERV_Type types,const SConnNetInfo * net_info,const SSERVICE_Extra * extra,const STimeout * timeout,size_t buf_size)820 CConn_ServiceStream::CConn_ServiceStream(const string& service,
821 TSERV_Type types,
822 const SConnNetInfo* net_info,
823 const SSERVICE_Extra* extra,
824 const STimeout* timeout,
825 size_t buf_size)
826 : CConn_IOStream(s_ServiceConnectorBuilder(service.c_str(),
827 types,
828 net_info,
829 0, // user_header
830 extra,
831 &m_CBD,
832 extra && extra->reset
833 ? x_Reset : 0,
834 extra && extra->adjust
835 ? x_Adjust : 0,
836 extra && extra->cleanup
837 ? x_Cleanup : 0,
838 x_ParseHeader,
839 extra && extra->get_next_info
840 ? x_GetNextInfo : 0,
841 timeout),
842 timeout, buf_size,
843 types & fSERV_DelayOpen ? fConn_DelayOpen : 0)
844 {
845 return;
846 }
847
848
CConn_ServiceStream(const string & service,const string & user_header,TSERV_Type types,const SSERVICE_Extra * extra,const STimeout * timeout,size_t buf_size)849 CConn_ServiceStream::CConn_ServiceStream(const string& service,
850 const string& user_header,
851 TSERV_Type types,
852 const SSERVICE_Extra* extra,
853 const STimeout* timeout,
854 size_t buf_size)
855 : CConn_IOStream(s_ServiceConnectorBuilder(service.c_str(),
856 types,
857 0, // net_info
858 user_header.c_str(),
859 extra,
860 &m_CBD,
861 extra && extra->reset
862 ? x_Reset : 0,
863 extra && extra->adjust
864 ? x_Adjust : 0,
865 extra && extra->cleanup
866 ? x_Cleanup : 0,
867 x_ParseHeader,
868 extra && extra->get_next_info
869 ? x_GetNextInfo : 0,
870 timeout),
871 timeout, buf_size,
872 types & fSERV_DelayOpen ? fConn_DelayOpen : 0)
873 {
874 return;
875 }
876
877
~CConn_ServiceStream()878 CConn_ServiceStream::~CConn_ServiceStream()
879 {
880 // Explicitly destroy so that the callbacks are not called out of context
881 x_Destroy();
882 }
883
884
x_ParseHeader(const char * header,void * data,int code)885 EHTTP_HeaderParse CConn_ServiceStream::x_ParseHeader(const char* header,
886 void* data, int code)
887 {
888 SSERVICE_CBData* cbd = reinterpret_cast<SSERVICE_CBData*>(data);
889 EHTTP_HeaderParse rv = cbd->status.Parse(header);
890 if (rv != eHTTP_HeaderSuccess)
891 return rv;
892 _ASSERT(!code || code == cbd->status.m_Code);
893 return cbd->extra.parse_header
894 ? cbd->extra.parse_header(header, cbd->extra.data, code)
895 : eHTTP_HeaderSuccess;
896 }
897
898
x_Adjust(SConnNetInfo * net_info,void * data,unsigned int count)899 int/*bool*/ CConn_ServiceStream::x_Adjust(SConnNetInfo* net_info,
900 void* data,
901 unsigned int count)
902 {
903 SSERVICE_CBData* cbd = reinterpret_cast<SSERVICE_CBData*>(data);
904 if (count != (unsigned int)(-1))
905 cbd->status.Clear(); // never call from CConn_ServiceStream ctor!
906 return cbd->extra.adjust(net_info, cbd->extra.data, count);
907 }
908
909
x_Reset(void * data)910 void CConn_ServiceStream::x_Reset(void* data)
911 {
912 SSERVICE_CBData* cbd = reinterpret_cast<SSERVICE_CBData*>(data);
913 cbd->extra.reset(cbd->extra.data);
914 }
915
916
x_Cleanup(void * data)917 void CConn_ServiceStream::x_Cleanup(void* data)
918 {
919 SSERVICE_CBData* cbd = reinterpret_cast<SSERVICE_CBData*>(data);
920 cbd->extra.cleanup(cbd->extra.data);
921 }
922
923
x_GetNextInfo(void * data,SERV_ITER iter)924 const SSERV_Info* CConn_ServiceStream::x_GetNextInfo(void* data,SERV_ITER iter)
925 {
926 SSERVICE_CBData* cbd = reinterpret_cast<SSERVICE_CBData*>(data);
927 return cbd->extra.get_next_info(cbd->extra.data, iter);
928 }
929
930
CConn_MemoryStream(size_t buf_size)931 CConn_MemoryStream::CConn_MemoryStream(size_t buf_size)
932 : CConn_IOStream(TConnector(MEMORY_CreateConnector()),
933 kInfiniteTimeout/*0*/, buf_size),
934 m_Ptr(0)
935 {
936 return;
937 }
938
939
CConn_MemoryStream(BUF buf,EOwnership owner,size_t buf_size)940 CConn_MemoryStream::CConn_MemoryStream(BUF buf,
941 EOwnership owner,
942 size_t buf_size)
943 : CConn_IOStream(TConnector(MEMORY_CreateConnectorEx(buf,
944 owner
945 == eTakeOwnership
946 ? 1/*true*/
947 : 0/*false*/)),
948 kInfiniteTimeout/*0*/, buf_size, 0/*flags*/,
949 0, BUF_Size(buf)),
950 m_Ptr(0)
951 {
952 return;
953 }
954
955
CConn_MemoryStream(const void * ptr,size_t size,EOwnership owner,size_t buf_size)956 CConn_MemoryStream::CConn_MemoryStream(const void* ptr,
957 size_t size,
958 EOwnership owner,
959 size_t buf_size)
960 : CConn_IOStream(TConnector(MEMORY_CreateConnector()),
961 kInfiniteTimeout/*0*/, buf_size, 0/*flags*/,
962 (CT_CHAR_TYPE*) ptr, size),
963 m_Ptr(owner == eTakeOwnership ? ptr : 0)
964 {
965 return;
966 }
967
968
~CConn_MemoryStream()969 CConn_MemoryStream::~CConn_MemoryStream()
970 {
971 // Explicitly call x_Destroy() to avoid using deleted m_Ptr otherwise.
972 x_Destroy();
973 delete[] (CT_CHAR_TYPE*) m_Ptr;
974 }
975
976
ToString(string * str)977 void CConn_MemoryStream::ToString(string* str)
978 {
979 if (!str) {
980 NCBI_THROW(CIO_Exception, eInvalidArg,
981 "CConn_MemoryStream::ToString(NULL) is not allowed");
982 }
983 CConn_Streambuf* sb = dynamic_cast<CConn_Streambuf*>(rdbuf());
984 size_t size = sb && good() ? (size_t)(tellp() - tellg()) : 0;
985 str->resize(size);
986 if (sb) {
987 size_t s = (size_t) sb->sgetn(&(*str)[0], size);
988 #ifdef NCBI_COMPILER_WORKSHOP
989 if (s < 0) {
990 s = 0; // WS6 weirdness to sometimes return -1 from sgetn() :-/
991 } else
992 #endif //NCBI_COMPILER_WORKSHOP
993 _ASSERT(s == size);
994 str->resize(s); // NB: just in case, essentially NOOP when s == size
995 }
996 }
997
998
ToVector(vector<char> * vec)999 void CConn_MemoryStream::ToVector(vector<char>* vec)
1000 {
1001 if (!vec) {
1002 NCBI_THROW(CIO_Exception, eInvalidArg,
1003 "CConn_MemoryStream::ToVector(NULL) is not allowed");
1004 }
1005 CConn_Streambuf* sb = dynamic_cast<CConn_Streambuf*>(rdbuf());
1006 size_t size = sb && good() ? (size_t)(tellp() - tellg()) : 0;
1007 vec->resize(size);
1008 if (sb) {
1009 size_t s = (size_t) sb->sgetn(&(*vec)[0], size);
1010 #ifdef NCBI_COMPILER_WORKSHOP
1011 if (s < 0) {
1012 s = 0; // WS6 weirdness to sometimes return -1 from sgetn() :-/
1013 } else
1014 #endif //NCBI_COMPILER_WORKSHOP
1015 _ASSERT(s == size);
1016 vec->resize(s); // NB: just in case, essentially NOOP when s == size
1017 }
1018 }
1019
1020
1021 static CConn_IOStream::TConnector
s_PipeConnectorBuilder(const string & cmd,const vector<string> & args,CPipe::TCreateFlags flgs,size_t pipe_size,CPipe * & pipe)1022 s_PipeConnectorBuilder(const string& cmd,
1023 const vector<string>& args,
1024 CPipe::TCreateFlags flgs,
1025 size_t pipe_size,
1026 CPipe*& pipe)
1027 {
1028 pipe = new CPipe(pipe_size);
1029 CONNECTOR c = PIPE_CreateConnector(cmd, args, flgs, pipe, eNoOwnership);
1030 return CConn_IOStream::TConnector(c);
1031 }
1032
1033
CConn_PipeStream(const string & cmd,const vector<string> & args,CPipe::TCreateFlags flgs,size_t pipe_size,const STimeout * timeout,size_t buf_size)1034 CConn_PipeStream::CConn_PipeStream(const string& cmd,
1035 const vector<string>& args,
1036 CPipe::TCreateFlags flgs,
1037 size_t pipe_size,
1038 const STimeout* timeout,
1039 size_t buf_size)
1040 : CConn_IOStream(s_PipeConnectorBuilder(cmd, args, flgs, pipe_size,
1041 m_Pipe),
1042 timeout, buf_size), m_ExitCode(-1)
1043 {
1044 return;
1045 }
1046
1047
~CConn_PipeStream()1048 CConn_PipeStream::~CConn_PipeStream()
1049 {
1050 // Explicitly do x_Destroy() to avoid using dead m_Pipe in base class dtor
1051 x_Destroy();
1052 delete m_Pipe;
1053 }
1054
1055
Close(void)1056 EIO_Status CConn_PipeStream::Close(void)
1057 {
1058 if (!flush())
1059 return Status(eIO_Write);
1060 // NB: This can lead to wrong order for a close callback, if any fired by
1061 // CConn_IOStream::Close(): the callback will be late and actually coming
1062 // _after_ the pipe gets closed here. There's no easy way to avoid this...
1063 EIO_Status status = m_Pipe->Close(&m_ExitCode);
1064 (void) CConn_IOStream::Close();
1065 return status;
1066 }
1067
1068
CConn_NamedPipeStream(const string & pipename,size_t pipesize,const STimeout * timeout,size_t buf_size)1069 CConn_NamedPipeStream::CConn_NamedPipeStream(const string& pipename,
1070 size_t pipesize,
1071 const STimeout* timeout,
1072 size_t buf_size)
1073 : CConn_IOStream(TConnector(NAMEDPIPE_CreateConnector(pipename, pipesize)),
1074 timeout, buf_size)
1075 {
1076 return;
1077 }
1078
1079
1080 static CConn_IOStream::TConnector
s_FtpConnectorBuilder(const SConnNetInfo * net_info,TFTP_Flags flgs,const SFTP_Callback * cmcb,const STimeout * timeout)1081 s_FtpConnectorBuilder(const SConnNetInfo* net_info,
1082 TFTP_Flags flgs,
1083 const SFTP_Callback* cmcb,
1084 const STimeout* timeout)
1085 {
1086 _ASSERT(net_info);
1087 if (timeout == kDefaultTimeout)
1088 timeout = net_info->timeout;
1089 const SConnNetInfo* x_net_info;
1090 if (timeout != net_info->timeout) {
1091 SConnNetInfo* xx_net_info = ConnNetInfo_Clone(net_info);
1092 if (xx_net_info)
1093 xx_net_info->timeout = timeout;
1094 x_net_info = xx_net_info;
1095 } else
1096 x_net_info = net_info;
1097 CONNECTOR c = FTP_CreateConnector(x_net_info, flgs, cmcb);
1098 if (x_net_info != net_info)
1099 ConnNetInfo_Destroy((SConnNetInfo*) x_net_info);
1100 return CConn_IOStream::TConnector(c);
1101 }
1102
1103
1104 /* For data integrity and unambigous interpretation, FTP streams are not
1105 * buffered at the level of the C++ STL streambuf because of the way they
1106 * execute read / write operations on the mix of FTP commands and data.
1107 * There should be a little impact on performance of byte-by-byte I/O (such as
1108 * formatted input, which is not expected very often for this kind of streams,
1109 * anyways), and almost none for block I/O (such as read / readsome / write).
1110 */
CConn_FtpStream(const string & host,const string & user,const string & pass,const string & path,unsigned short port,TFTP_Flags flgs,const SFTP_Callback * cmcb,const STimeout * timeout,size_t buf_size)1111 CConn_FtpStream::CConn_FtpStream(const string& host,
1112 const string& user,
1113 const string& pass,
1114 const string& path,
1115 unsigned short port,
1116 TFTP_Flags flgs,
1117 const SFTP_Callback* cmcb,
1118 const STimeout* timeout,
1119 size_t buf_size)
1120 : CConn_IOStream(TConnector(FTP_CreateConnectorSimple(host.c_str(),
1121 port,
1122 user.c_str(),
1123 pass.c_str(),
1124 path.c_str(),
1125 flgs,
1126 cmcb)),
1127 timeout, buf_size,
1128 fConn_Untie | fConn_WriteUnbuffered)
1129 {
1130 return;
1131 }
1132
1133
CConn_FtpStream(const SConnNetInfo & net_info,TFTP_Flags flgs,const SFTP_Callback * cmcb,const STimeout * timeout,size_t buf_size)1134 CConn_FtpStream::CConn_FtpStream(const SConnNetInfo& net_info,
1135 TFTP_Flags flgs,
1136 const SFTP_Callback* cmcb,
1137 const STimeout* timeout,
1138 size_t buf_size)
1139 : CConn_IOStream(s_FtpConnectorBuilder(&net_info,
1140 flgs,
1141 cmcb,
1142 timeout),
1143 timeout, buf_size,
1144 fConn_Untie | fConn_WriteUnbuffered)
1145 {
1146 return;
1147 }
1148
1149
Drain(const STimeout * timeout)1150 EIO_Status CConn_FtpStream::Drain(const STimeout* timeout)
1151 {
1152 const STimeout* r_timeout = kInfiniteTimeout/*0*/;
1153 const STimeout* w_timeout = kInfiniteTimeout/*0*/;
1154 static char sink[16384]; /*NB:shared sink*/
1155 CONN conn = GetCONN();
1156 if (conn) {
1157 size_t n;
1158 r_timeout = CONN_GetTimeout(conn, eIO_Read);
1159 w_timeout = CONN_GetTimeout(conn, eIO_Write);
1160 _VERIFY(SetTimeout(eIO_Read, timeout) == eIO_Success);
1161 _VERIFY(SetTimeout(eIO_Write, timeout) == eIO_Success);
1162 // Cause any upload-in-progress to abort
1163 CONN_Read(conn, sink, sizeof(sink), &n, eIO_ReadPlain);
1164 // Cause any command-in-progress to abort
1165 CONN_Write(conn, "NOOP\n", 5, &n, eIO_WritePersist);
1166 }
1167 clear();
1168 while (read(sink, sizeof(sink)))
1169 ;
1170 if (!conn)
1171 return eIO_Closed;
1172 EIO_Status status;
1173 do {
1174 size_t n;
1175 status = CONN_Read(conn, sink, sizeof(sink), &n, eIO_ReadPersist);
1176 } while (status == eIO_Success);
1177 _VERIFY(CONN_SetTimeout(conn, eIO_Read, r_timeout) == eIO_Success);
1178 _VERIFY(CONN_SetTimeout(conn, eIO_Write, w_timeout) == eIO_Success);
1179 clear();
1180 return status == eIO_Closed ? eIO_Success : status;
1181 }
1182
1183
CConn_FTPDownloadStream(const string & host,const string & file,const string & user,const string & pass,const string & path,unsigned short port,TFTP_Flags flgs,const SFTP_Callback * cmcb,Uint8 offset,const STimeout * timeout,size_t buf_size)1184 CConn_FTPDownloadStream::CConn_FTPDownloadStream(const string& host,
1185 const string& file,
1186 const string& user,
1187 const string& pass,
1188 const string& path,
1189 unsigned short port,
1190 TFTP_Flags flgs,
1191 const SFTP_Callback* cmcb,
1192 Uint8 offset,
1193 const STimeout* timeout,
1194 size_t buf_size)
1195 : CConn_FtpStream(host, user, pass, path, port, flgs, cmcb,
1196 timeout, buf_size)
1197 {
1198 if (!file.empty())
1199 x_InitDownload(file, offset);
1200 }
1201
1202
CConn_FTPDownloadStream(const SConnNetInfo & net_info,TFTP_Flags flgs,const SFTP_Callback * cmcb,Uint8 offset,const STimeout * timeout,size_t buf_size)1203 CConn_FTPDownloadStream::CConn_FTPDownloadStream(const SConnNetInfo& net_info,
1204 TFTP_Flags flgs,
1205 const SFTP_Callback* cmcb,
1206 Uint8 offset,
1207 const STimeout* timeout,
1208 size_t buf_size)
1209 : CConn_FtpStream(net_info, flgs | fFTP_IgnorePath, cmcb,
1210 timeout, buf_size)
1211 {
1212 if (net_info.path[0])
1213 x_InitDownload(net_info.path, offset);
1214 }
1215
1216
x_InitDownload(const string & file,Uint8 offset)1217 void CConn_FTPDownloadStream::x_InitDownload(const string& file, Uint8 offset)
1218 {
1219 // Use '\n' here instead of NcbiFlush to avoid (and thus make silent)
1220 // flush errors on retrieval of nonexistent (or bad) files / directories...
1221 EIO_Status status;
1222 if (offset) {
1223 write("REST ", 5) << NStr::UInt8ToString(offset) << '\n';
1224 status = Status(eIO_Write);
1225 } else
1226 status = eIO_Success;
1227 if (good() && status == eIO_Success) {
1228 bool directory = NStr::EndsWith(file, '/');
1229 write(directory ? "NLST " : "RETR ", 5) << file << '\n';
1230 status = Status(eIO_Write);
1231 }
1232 if (status != eIO_Success)
1233 clear(NcbiBadbit);
1234 }
1235
1236
CConn_FTPUploadStream(const string & host,const string & user,const string & pass,const string & file,const string & path,unsigned short port,TFTP_Flags flgs,Uint8 offset,const STimeout * timeout)1237 CConn_FTPUploadStream::CConn_FTPUploadStream(const string& host,
1238 const string& user,
1239 const string& pass,
1240 const string& file,
1241 const string& path,
1242 unsigned short port,
1243 TFTP_Flags flgs,
1244 Uint8 offset,
1245 const STimeout* timeout)
1246 : CConn_FtpStream(host, user, pass, path, port, flgs, 0/*cmcb*/,
1247 timeout)
1248 {
1249 if (!file.empty())
1250 x_InitUpload(file, offset);
1251 }
1252
1253
CConn_FTPUploadStream(const SConnNetInfo & net_info,TFTP_Flags flgs,Uint8 offset,const STimeout * timeout)1254 CConn_FTPUploadStream::CConn_FTPUploadStream(const SConnNetInfo& net_info,
1255 TFTP_Flags flgs,
1256 Uint8 offset,
1257 const STimeout* timeout)
1258 : CConn_FtpStream(net_info, flgs | fFTP_IgnorePath, 0/*cmcb*/,
1259 timeout)
1260 {
1261 if (net_info.path[0])
1262 x_InitUpload(net_info.path, offset);
1263 }
1264
1265
x_InitUpload(const string & file,Uint8 offset)1266 void CConn_FTPUploadStream::x_InitUpload(const string& file, Uint8 offset)
1267 {
1268 EIO_Status status;
1269 if (offset) {
1270 write("REST ", 5) << NStr::UInt8ToString(offset) << NcbiFlush;
1271 status = Status(eIO_Write);
1272 } else
1273 status = eIO_Success;
1274 if (good() && status == eIO_Success) {
1275 write("STOR ", 5) << file << NcbiFlush;
1276 status = Status(eIO_Write);
1277 }
1278 if (status != eIO_Success)
1279 clear(NcbiBadbit);
1280 }
1281
1282
1283 /* non-public class */
1284 class CConn_FileStream : public CConn_IOStream
1285 {
1286 public:
CConn_FileStream(const string & ifname,const string & ofname=kEmptyStr,SFILE_ConnAttr * attr=0)1287 CConn_FileStream(const string& ifname,
1288 const string& ofname = kEmptyStr,
1289 SFILE_ConnAttr* attr = 0)
1290 : CConn_IOStream(TConnector(FILE_CreateConnectorEx(ifname.c_str(),
1291 ofname.c_str(),
1292 attr)),
1293 0/*timeout*/, 0/*unbuffered*/,
1294 fConn_Untie)
1295 {
1296 return;
1297 }
1298 };
1299
1300
x_IsIdentifier(const string & str)1301 static bool x_IsIdentifier(const string& str)
1302 {
1303 const char* s = str.c_str();
1304 if (!isalpha((unsigned char)(*s)))
1305 return false;
1306 for (++s; *s; ++s) {
1307 if (!isalnum((unsigned char)(*s)) && *s != '_')
1308 return false;
1309 }
1310 return true;
1311 }
1312
1313
NcbiOpenURL(const string & url,size_t buf_size)1314 extern CConn_IOStream* NcbiOpenURL(const string& url, size_t buf_size)
1315 {
1316 {
1317 class CInPlaceConnIniter : protected CConnIniter
1318 {
1319 } conn_initer; /*NCBI_FAKE_WARNING*/
1320 }
1321 bool svc = x_IsIdentifier(url);
1322
1323 AutoPtr<SConnNetInfo> net_info
1324 (ConnNetInfo_CreateInternal
1325 (svc
1326 ? TTempCharPtr(SERV_ServiceName(url.c_str())).get()
1327 : NStr::StartsWith(url, "ftp://", NStr::eNocase)
1328 ? "_FTP" : 0));
1329 if (svc)
1330 return new CConn_ServiceStream(url, fSERV_Any, net_info.get());
1331
1332 unsigned int host;
1333 unsigned short port;
1334 if (url.size() == CSocketAPI::StringToHostPort(url, &host, &port)
1335 && port && net_info.get()) {
1336 net_info->req_method = eReqMethod_Connect;
1337 }
1338
1339 if (ConnNetInfo_ParseURL(net_info.get(), url.c_str())) {
1340 _ASSERT(net_info); // otherwise ConnNetInfo_ParseURL() would've failed
1341 if (net_info->req_method == eReqMethod_Connect) {
1342 return new CConn_SocketStream(*net_info, 0, 0, fSOCK_LogDefault,
1343 net_info->timeout, buf_size);
1344 }
1345 switch (net_info->scheme) {
1346 case eURL_Https:
1347 case eURL_Http:
1348 return new CConn_HttpStream(net_info.get(), kEmptyStr, 0, 0, 0, 0,
1349 fHTTP_AutoReconnect,
1350 kDefaultTimeout, buf_size);
1351 case eURL_File:
1352 if (*net_info->host || net_info->port)
1353 break; // not supported
1354 if (net_info->debug_printout) {
1355 // manual cleanup of most fields req'd
1356 net_info->req_method = eReqMethod_Any;
1357 net_info->external = 0;
1358 net_info->firewall = 0;
1359 net_info->stateless = 0;
1360 net_info->lb_disable = 0;
1361 net_info->http_version = 0;
1362 net_info->http_push_auth = 0;
1363 net_info->http_proxy_leak = 0;
1364 net_info->user[0] = '\0';
1365 net_info->pass[0] = '\0';
1366 net_info->http_proxy_host[0] = '\0';
1367 net_info->http_proxy_port = 0;
1368 net_info->http_proxy_user[0] = '\0';
1369 net_info->http_proxy_pass[0] = '\0';
1370 net_info->max_try = 0;
1371 net_info->timeout = kInfiniteTimeout/*0*/;
1372 ConnNetInfo_SetUserHeader(net_info.get(), 0);
1373 if (net_info->http_referer) {
1374 free((void*) net_info->http_referer);
1375 net_info->http_referer = 0;
1376 }
1377 ConnNetInfo_Log(net_info.get(), eLOG_Note, CORE_GetLOG());
1378 }
1379 return new CConn_FileStream(net_info->path);
1380 case eURL_Ftp:
1381 if (!net_info->user[0]) {
1382 strcpy(net_info->user, "ftp");
1383 if (!net_info->pass[0])
1384 strcpy(net_info->pass, "-none@");
1385 }
1386 return new CConn_FTPDownloadStream(*net_info, 0, 0, 0,
1387 net_info->timeout, buf_size);
1388 default:
1389 break;
1390 }
1391 }
1392 return 0;
1393 }
1394
1395
1396 END_NCBI_SCOPE
1397