NCBI C++ ToolKit
ncbi_http_session.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: ncbi_http_session.cpp 100701 2023-08-31 19:21:12Z lavr $
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: Aleksey Grichenko
27  *
28  * File Description:
29  * CHttpSession class supporting different types of requests/responses,
30  * headers/cookies/sessions management etc.
31  *
32  */
33 
34 #include <ncbi_pch.hpp>
35 #include "ncbi_ansi_ext.h"
36 #include "ncbi_servicep.h"
37 #include <corelib/ncbifile.hpp>
38 #include <corelib/request_ctx.hpp>
39 #include <corelib/ncbimtx.hpp>
40 #include <corelib/ncbistr.hpp>
42 #include <stdlib.h>
43 
44 
46 
47 
48 ///////////////////////////////////////////////////////
49 // CHttpHeaders::
50 //
51 
52 static const char* kHttpHeaderNames[] = {
53  "Cache-Control",
54  "Content-Length",
55  "Content-Type",
56  "Cookie",
57  "Date",
58  "Expires",
59  "Location",
60  "Range",
61  "Referer",
62  "Set-Cookie",
63  "User-Agent",
64  "Host"
65 };
66 
67 
68 // Reserved headers cannot be set manually.
69 static const char* kReservedHeaders[] = {
70  "NCBI-SID",
71  "NCBI-PHID"
72 };
73 
75 static const char kHttpHeaderDelimiter = ':';
76 
77 
79 {
80  _ASSERT(size_t(name)<sizeof(kHttpHeaderNames)/sizeof(kHttpHeaderNames[0]));
81  return kHttpHeaderNames[name];
82 }
83 
84 
86 {
87  return m_Headers.find(name.GetName()) != m_Headers.end();
88 }
89 
90 
92 {
94  if (it == m_Headers.end()) return 0;
95  return it->second.size();
96 }
97 
98 
100 {
102  if (it == m_Headers.end() || it->second.empty()) return kEmptyStr;
103  return it->second.back();
104 }
105 
106 
109 {
111  if (it == m_Headers.end()) return kEmptyValues.Get();
112  return it->second;
113 }
114 
115 
118 {
119  _VERIFY( !x_IsReservedHeader(name.GetName()) );
120  THeaderValues& vals = m_Headers[name.GetName()];
121  vals.clear();
122  vals.push_back(value);
123 }
124 
125 
128 {
129  _VERIFY( !x_IsReservedHeader(name.GetName()) );
130  m_Headers[name.GetName()].push_back(value);
131 }
132 
133 
135 {
137  if (it != m_Headers.end()) {
138  it->second.clear();
139  }
140 }
141 
142 
144 {
145  m_Headers.clear();
146 }
147 
148 
150 {
151  list<CTempString> lines;
152  NStr::Split(from, HTTP_EOL, lines,
154 
155  string name, value;
156  ITERATE(list<CTempString>, line, lines) {
157  size_t delim = line->find(kHttpHeaderDelimiter);
158  if (delim == NPOS || delim < 1) {
159  // No delimiter or no name before the delimiter - skip the line.
160  // Can be HTTP status or an empty line.
161  continue;
162  }
163  name = line->substr(0, delim);
164  value = line->substr(delim + 1);
166  to[name].push_back(value);
167  }
168 }
169 
170 
171 // Have to keep this method as it may be used by some clients
173 {
174  s_ParseHttpHeader(headers, m_Headers);
175 }
176 
177 
178 string CHttpHeaders::GetHttpHeader(void) const
179 {
180  string ret;
181  ITERATE(THeaders, hdr, m_Headers) {
182  ITERATE(THeaderValues, val, hdr->second) {
183  ret += hdr->first + kHttpHeaderDelimiter + " " + *val + HTTP_EOL;
184  }
185  }
186  return ret;
187 }
188 
189 
190 void CHttpHeaders::Assign(const CHttpHeaders& headers)
191 {
192  m_Headers.clear();
193  Merge(headers);
194 }
195 
196 
197 void CHttpHeaders::Merge(const CHttpHeaders& headers)
198 {
199  ITERATE(THeaders, name, headers.m_Headers) {
200  m_Headers[name->first].assign(
201  name->second.begin(), name->second.end());
202  }
203 }
204 
205 
207 {
208  for (size_t i = 0; i < sizeof(kReservedHeaders)/sizeof(kReservedHeaders[0]); ++i) {
209  if (!NStr::CompareNocase(name, kReservedHeaders[i])) {
210  ERR_POST(kReservedHeaders[i] << " must be set through CRequestContext");
211  return true;
212  }
213  }
214  return false;
215 }
216 
217 
218 ///////////////////////////////////////////////////////
219 // CHttpFormData::
220 //
221 // See http://www.w3.org/TR/html401/interact/forms.html
222 // and http://tools.ietf.org/html/rfc2388
223 //
224 
225 
226 const char* kContentType_FormUrlEnc = "application/x-www-form-urlencoded";
227 const char* kContentType_MultipartFormData = "multipart/form-data";
228 
229 
231  : m_ContentType(eFormUrlEncoded),
232  m_Boundary(CreateBoundary())
233 {
234 }
235 
236 
238 {
239  if (!m_Providers.empty() && content_type != eMultipartFormData) {
240  NCBI_THROW(CHttpSessionException, eBadContentType,
241  "Requested Content-Type cannot be used with the form data");
242  }
243  m_ContentType = content_type;
244 }
245 
246 
249  CTempString content_type)
250 {
251  if ( entry_name.empty() ) {
252  NCBI_THROW(CHttpSessionException, eBadFormDataName,
253  "Form data entry name must not be empty");
254  }
255  TValues& values = m_Entries[entry_name];
256  SFormData entry;
257  entry.m_Value = value;
258  entry.m_ContentType = content_type;
259  values.push_back(entry);
260 }
261 
262 
264  CFormDataProvider_Base* provider)
265 {
266  if ( entry_name.empty() ) {
267  NCBI_THROW(CHttpSessionException, eBadFormDataName,
268  "Form data entry name must not be empty");
269  }
271  m_Providers[entry_name].push_back(Ref(provider));
272 }
273 
274 
276 {
277 public:
279  const string& content_type)
281  m_ContentType(content_type) {}
282 
283  virtual ~CFileDataProvider(void) {}
284 
285  virtual string GetContentType(void) const { return m_ContentType; }
286 
287  virtual string GetFileName(void) const
288  {
289  CFile f(m_FileName);
290  return f.GetName();
291  }
292 
293  virtual void WriteData(CNcbiOstream& out) const
294  {
295  try {
296  CNcbiIfstream in(m_FileName.c_str(), ios_base::binary);
298  }
299  catch (...) {
300  NCBI_THROW(CHttpSessionException, eBadFormData,
301  "Failed to POST file " + m_FileName);
302  }
303  }
304 
305 private:
306  string m_FileName;
308 };
309 
310 
313  CTempString content_type)
314 {
315  AddProvider(entry_name, new CFileDataProvider(file_name, content_type));
316 }
317 
318 
320 {
322  m_Entries.clear();
323  m_Providers.clear();
325 }
326 
327 
329 {
330  string ret;
331  switch ( m_ContentType ) {
332  case eFormUrlEncoded:
334  break;
335  case eMultipartFormData:
336  // Main boundary must be sent with global headers.
337  _ASSERT( !m_Boundary.empty() );
339  ret += "; boundary=" + m_Boundary;
340  }
341  return ret;
342 }
343 
344 
345 // CRandom creates an extra-dependency, so use a simple random number
346 // generator sufficient for creating boundaries.
348 {
349  static Int8 last = Int8(time(0));
350  const Int8 a = 1103515245;
351  const Int8 c = 12345;
352  const Int8 m = 1 << 16;
353  last = (a * last + c) % m;
354  return last % range;
355 }
356 
357 
359 {
360  static const char kBoundaryChars[] =
361  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-";
362  static const size_t kBoundaryCharsLen = sizeof(kBoundaryChars) - 1;
363  static const int kBoundaryLen = 32;
364 
365  string boundary;
366  for (int i = 0; i < kBoundaryLen; ++i) {
367  boundary += kBoundaryChars[s_SimpleRand(kBoundaryCharsLen)];
368  }
369  return boundary;
370 }
371 
372 
373 // More accurate encoder than the default one.
375 {
376 public:
377  virtual string EncodeArgName(const string& name) const
379  virtual string EncodeArgValue(const string& value) const
381 };
382 
383 
385  const string& boundary,
386  const string& name,
387  const string& content_type,
388  const string& filename = kEmptyStr)
389 {
390  out << "--" << boundary << HTTP_EOL;
391  out << "Content-Disposition: form-data; name=\"" << name << "\"";
392  if ( !filename.empty() ) {
393  out << "; filename=\"" << filename << "\"";
394  }
395  out << HTTP_EOL;
396  if ( !content_type.empty() ) {
397  out << "Content-Type: " <<
398  content_type << HTTP_EOL;
399  }
400  out << HTTP_EOL;
401 }
402 
403 
405 {
408  // Format data as query string.
409  CUrlArgs args;
410  ITERATE(TEntries, values, m_Entries) {
411  if (values->second.size() > 1) {
412  NCBI_THROW(CHttpSessionException, eBadFormData,
413  "Multiple values not allowed in URL-encoded form data, "
414  " entry '" + values->first + '\'');
415  }
416  args.SetValue(values->first, values->second.back().m_Value);
417  }
418  CFormDataEncoder encoder;
419  out << args.GetQueryString(CUrlArgs::eAmp_Char, &encoder);
420  }
421  else {
422  // eMultipartFormData
423  _ASSERT(!m_Boundary.empty());
424  ITERATE(TEntries, values, m_Entries) {
425  ITERATE(TValues, entry, values->second) {
426  x_WritePartHeader(out, m_Boundary, values->first,
427  entry->m_ContentType);
428  out << entry->m_Value << HTTP_EOL;
429  }
430  }
431  ITERATE(TProviderEntries, providers, m_Providers) {
432  if ( providers->second.empty() ) continue;
433  string part_boundary = CreateBoundary();
434  string part_content_type = "multipart/mixed; boundary=";
435  part_content_type += part_boundary;
436  x_WritePartHeader(out, m_Boundary, providers->first,
437  part_content_type);
438  ITERATE(TProviders, provider, providers->second) {
439  x_WritePartHeader(out, part_boundary, providers->first,
440  (*provider)->GetContentType(),
441  (*provider)->GetFileName());
442  (*provider)->WriteData(out);
443  out << HTTP_EOL;
444  }
445  // End of part
446  out << "--" << part_boundary << "--" << HTTP_EOL;
447  }
448  // End of form
449  out << "--" << m_Boundary << "--" << HTTP_EOL;
450  }
451 }
452 
453 
454 bool CHttpFormData::IsEmpty(void) const
455 {
456  return !m_Entries.empty() || !m_Providers.empty();
457 }
458 
459 
460 ///////////////////////////////////////////////////////
461 // CHttpResponse::
462 //
463 
464 
466  const CUrl& url,
467  shared_ptr<iostream> stream)
468  : m_Session(&session),
469  m_Url(url),
470  m_Location(url),
471  m_Stream(std::move(stream)),
472  m_Headers(new CHttpHeaders),
473  m_StatusCode(0)
474 {
475 }
476 
477 
479 {
480  _ASSERT(m_Stream);
481  if ( !CanGetContentStream() ) {
482  NCBI_THROW(CHttpSessionException, eBadStream,
483  "Content stream not available for status '"
485  + m_StatusText + '\'');
486  }
487  return *m_Stream;
488 }
489 
490 
492 {
493  _ASSERT(m_Stream);
494  if ( CanGetContentStream() ) {
495  NCBI_THROW(CHttpSessionException, eBadStream,
496  "Error stream not available for status '"
498  + m_StatusText + '\'');
499  }
500  return *m_Stream;
501 }
502 
503 
505 {
506  return 200 <= m_StatusCode && m_StatusCode < 300;
507 }
508 
509 
510 void CHttpResponse::x_Update(CHttpHeaders::THeaders headers, int status_code, string status_text)
511 {
512  m_Headers->m_Headers.swap(headers);
513  m_StatusCode = status_code;
514  m_StatusText = std::move(status_text);
515  const auto& cookies = m_Headers->GetAllValues(CHttpHeaders::eSetCookie);
516  m_Session->x_SetCookies(cookies, &m_Location);
517 }
518 
519 
520 ///////////////////////////////////////////////////////
521 // CTlsCertCredentials::
522 //
523 
524 
526  : m_Cert(cert), m_PKey(pkey)
527 {
528  if (cert.HasZeroAtEnd()) m_Cert.push_back('\0');
529  if (pkey.HasZeroAtEnd()) m_PKey.push_back('\0');
530 }
531 
533 {
534  if ( m_Cred ) {
536  }
537 }
538 
539 
541 {
542  if ( !m_Cred ) {
543  m_Cred = NcbiCreateTlsCertCredentials(m_Cert.data(), m_Cert.size(), m_PKey.data(), m_PKey.size());
544  }
545  return m_Cred;
546 }
547 
548 
549 ///////////////////////////////////////////////////////
550 // CHttpRequest::
551 //
552 
553 
554 unsigned short SGetHttpDefaultRetries::operator()(void) const
555 {
556  char buf[16];
559  int maxtry = atoi(buf);
560  return (unsigned short)(maxtry ? maxtry - 1 : 0);
561 }
562 
563 
564 inline
565 unsigned short x_RetriesToMaxtry(unsigned short retries)
566 {
567  return (unsigned short)(++retries ? retries : retries - 1);
568 }
569 
570 
572  const CUrl& url,
573  EReqMethod method,
574  const CHttpParam& param)
575  : m_Session(&session),
576  m_Url(url),
577  m_Method(method),
578  m_Headers(new CHttpHeaders),
579  m_AdjustUrl(0),
580  m_Credentials(session.GetCredentials())
581 {
582  SetParam(param);
583 }
584 
585 
586 // Processing logic for 'retry later' response in CHttpRequest::Execute()
588 {
589  SRetryProcessing(ESwitch on_off, const CTimeout& deadline, CUrl& url,
590  EReqMethod& method, CRef<CHttpHeaders>& headers,
591  CRef<CHttpFormData>& form_data);
592 
593  bool operator()(const CHttpHeaders& headers);
594 
595 private:
596  // This class is used to restore CHttpRequest members to their original state
597  template <class TMember, class TValue = TMember>
599  {
600  TMember& value;
601 
602  SValueRestorer(TMember& v) : value(v) { Assign(original, value); }
604  void Restore() { Assign(value, original); }
605 
606  private:
607  TValue original;
608  };
609 
610  template <class TTo, class TFrom> static void Assign(TTo&, const TFrom&);
611 
612  const bool m_Enabled;
618 };
619 
620 
622  EReqMethod& method, CRef<CHttpHeaders>& headers,
623  CRef<CHttpFormData>& form_data) :
624  m_Enabled(on_off == eOn),
625  m_Deadline(deadline.IsDefault() ? CTimeout::eInfinite : deadline),
626  m_Url(url),
627  m_Method(method),
628  m_Headers(headers),
629  m_FormData(form_data)
630 {
631 }
632 
633 
635 {
636  // Must correspond to CHttpRetryContext (e.g. CCgi2RCgiApp) values
637  const unsigned long kExecuteDefaultRefreshDelay = 5;
638  const string kExecuteHeaderRetryURL = "X-NCBI-Retry-URL";
639  const string kExecuteHeaderRetryDelay = "X-NCBI-Retry-Delay";
640 
641  if (!m_Enabled) return false;
642  if (m_Deadline.IsExpired()) return false;
643 
644  const auto& retry_url = headers.GetValue(kExecuteHeaderRetryURL);
645 
646  // Not a 'retry later' response (e.g. remote CGI has already finished)
647  if (retry_url.empty()) return false;
648 
649  const auto& retry_delay = headers.GetValue(kExecuteHeaderRetryDelay);
650  unsigned long sleep_ms = kExecuteDefaultRefreshDelay;
651 
652  // HTTP header has delay
653  if (!retry_delay.empty()) {
654  try {
655  sleep_ms = NStr::StringToULong(retry_delay) * kMilliSecondsPerSecond;
656  }
657  catch (CStringException& ex) {
658  if (ex.GetErrCode() != CStringException::eConvert) throw;
659  }
660  }
661 
662  // If the deadline is less than the retry delay,
663  // sleep for the remaining and check once again
664  try {
665  auto remaining = m_Deadline.GetRemainingTime().GetAsMilliSeconds();
666  if (remaining < sleep_ms) sleep_ms = remaining;
667  }
668  catch (CTimeException& ex) {
669  if (ex.GetErrCode() != CTimeException::eConvert) throw;
670  }
671 
672  SleepMilliSec(sleep_ms);
673 
674  // Make subsequent requests appropriate
675  m_Url.value = retry_url;
677  m_Headers.Restore();
679 
680  return true;
681 }
682 
683 
684 template <class TTo, class TFrom>
685 void SRetryProcessing::Assign(TTo& to, const TFrom& from)
686 {
687  to = from;
688 }
689 
690 
691 template <>
693 {
694  to.Assign(*from);
695 }
696 
697 
698 template <>
700 {
701  to->Assign(from);
702 }
703 
704 
706 {
710  auto protocol = m_Session->GetProtocol();
711 
712  do {
713  // Connection not open yet.
714  // Only POST and PUT support sending form data.
715  bool have_data = m_FormData && !m_FormData.Empty();
716  if ( !m_Response ) {
717  if (m_Stream) {
718  NCBI_THROW(CHttpSessionException, eBadRequest,
719  "Attempt to execute HTTP request already being executed");
720  }
721 
722  m_Session->x_StartRequest(protocol, *this, have_data);
723  }
725  _ASSERT(m_Stream);
726  if ( have_data ) {
728  }
729  // Send data to the server and close output stream.
730  m_Stream->peek();
731  m_Stream.reset();
732  ret = m_Response;
733  m_Response.Reset();
734  }
735  while (m_Session->x_Downgrade(*ret, protocol) || retry_processing(ret->Headers()));
736 
737  return *ret;
738 }
739 
740 
742 {
743  if ( !x_CanSendData() ) {
744  NCBI_THROW(CHttpSessionException, eBadRequest,
745  "Request method does not allow writing to the output stream");
746  }
747  if ( !m_Stream ) {
748  if (m_Response) {
749  NCBI_THROW(CHttpSessionException, eBadRequest,
750  "Attempt to execute HTTP request already being executed");
751  }
752 
753  m_Session->x_StartRequest(m_Session->GetProtocol(), *this, false);
754  }
756  _ASSERT(m_Stream);
757  return *m_Stream;
758 }
759 
760 
762 {
763  if ( !x_CanSendData() ) {
764  NCBI_THROW(CHttpSessionException, eBadRequest,
765  "Request method does not support sending data");
766  }
767  if ( m_Stream ) {
768  NCBI_THROW(CHttpSessionException, eBadRequest,
769  "Can not get form data while executing request");
770  }
771  if ( !m_FormData ) {
773  }
774  return *m_FormData;
775 }
776 
777 
778 void CHttpRequest::x_AdjustHeaders(bool use_form_data)
779 {
780  x_AddCookieHeader(m_Url, true);
781  if (use_form_data) {
784  }
785 }
786 
787 
788 void CHttpRequest::x_UpdateResponse(CHttpHeaders::THeaders headers, int status_code, string status_text)
789 {
790  if (m_Response) {
791  m_Response->x_Update(std::move(headers), status_code, std::move(status_text));
792  }
793 }
794 
795 
797 {
798  CHttpProxy proxy = GetProxy();
799  // Try per-session proxy, if any.
800  if ( proxy.IsEmpty() ) proxy = m_Session->GetProxy();
801  if ( proxy.IsEmpty() ) return;
802 
803  if (proxy.GetHost().size() > CONN_HOST_LEN) {
804  NCBI_THROW(CHttpSessionException, eConnFailed,
805  "Proxy host length exceeds " NCBI_AS_STRING(CONN_HOST_LEN));
806  }
807  memcpy(net_info.http_proxy_host, proxy.GetHost().c_str(), proxy.GetHost().size() + 1);
808  net_info.http_proxy_port = proxy.GetPort();
809 
810  if (proxy.GetUser().size() > CONN_USER_LEN) {
811  NCBI_THROW(CHttpSessionException, eConnFailed,
812  "Proxy user length exceeds " NCBI_AS_STRING(CONN_USER_LEN));
813  }
814  memcpy(net_info.http_proxy_user, proxy.GetUser().c_str(), proxy.GetUser().size() + 1);
815 
816  if (proxy.GetPassword().size() > CONN_PASS_LEN) {
817  NCBI_THROW(CHttpSessionException, eConnFailed,
818  "Proxy password length exceeds " NCBI_AS_STRING(CONN_PASS_LEN));
819  }
820  memcpy(net_info.http_proxy_pass, proxy.GetPassword().c_str(), proxy.GetPassword().size() + 1);
821 }
822 
823 
824 // Interface for the HTTP connector's adjust callback
825 struct SAdjustData {
826  CHttpRequest* m_Request; // NB: don't use after request has been sent!
828 
829  SAdjustData(CHttpRequest* request = 0)
830  : m_Request(request), m_IsService(false)
831  { }
832 };
833 
834 
835 static void s_Cleanup(void* user_data)
836 {
837  SAdjustData* adjust_data = reinterpret_cast<SAdjustData*>(user_data);
838  delete adjust_data;
839 }
840 
841 
842 void CHttpRequest::x_InitConnection(bool use_form_data)
843 {
844  bool is_service = m_Url.IsService();
845  unique_ptr<SConnNetInfo, void (*)(SConnNetInfo*)> net_info
846  (ConnNetInfo_Create(is_service ? m_Url.GetService().c_str() : 0),
848  if (!net_info || (is_service && !net_info->svc[0])) {
849  NCBI_THROW(CHttpSessionException, eConnFailed,
850  "Failed to create SConnNetInfo");
851  }
853  net_info->http_version = 1;
854  }
855  net_info->req_method = m_Method;
856 
857  // Set scheme if given in URL (only if http(s) since this is CHttpRequest).
858  string url_scheme(m_Url.GetScheme());
859  if (NStr::EqualNocase(url_scheme, "https")) {
860  net_info->scheme = eURL_Https;
861  }
862  else if (NStr::EqualNocase(url_scheme, "http")) {
863  net_info->scheme = eURL_Http;
864  }
865 
866  // Save headers set automatically (e.g. from CONN_HTTP_USER_HEADER).
867  if (net_info->http_user_header) {
868  s_ParseHttpHeader(net_info->http_user_header, m_Headers->m_Headers);
869  }
870 
871  x_AdjustHeaders(use_form_data);
872  string headers = m_Headers->GetHttpHeader();
873 
874  if ( !m_Timeout.IsDefault() ) {
875  STimeout sto;
877  }
878  if ( !m_Retries.IsNull() ) {
879  net_info->max_try = x_RetriesToMaxtry(m_Retries);
880  }
881  if ( m_Credentials ) {
882  net_info->credentials = m_Credentials->GetNcbiCred();
883  }
884  x_SetProxy(*net_info);
885 
887  unique_ptr<SAdjustData> adjust_data(new SAdjustData(this));
888  if ( !is_service ) {
889  // Connect using HTTP.
890  m_Stream.reset(new CConn_HttpStream(
892  net_info.get(),
893  headers.c_str(),
895  adjust_data.get(),
896  sx_Adjust,
897  s_Cleanup,
898  // Always set AdjustOnRedirect flag - to send correct cookies.
900  }
901  else {
902  // Try to resolve service name.
903  adjust_data->m_IsService = true;
904  SSERVICE_Extra x_extra;
905  memset(&x_extra, 0, sizeof(x_extra));
906  x_extra.data = adjust_data.get();
907  x_extra.adjust = sx_Adjust;
908  x_extra.cleanup = s_Cleanup;
909  x_extra.parse_header = sx_ParseHeader;
911  ConnNetInfo_OverrideUserHeader(net_info.get(), headers.c_str());
912  m_Stream.reset(new CConn_ServiceStream(
913  m_Url.GetService(), // Ignore other fields for now, set them in sx_Adjust (called with failure_count == -1 on open)
914  fSERV_Http,
915  net_info.get(),
916  &x_extra));
917  }
918  adjust_data.release();
920 }
921 
922 
923 void CHttpRequest::x_InitConnection2(shared_ptr<iostream> stream)
924 {
925  m_Stream = std::move(stream);
927 }
928 
929 
931 {
932  return m_Method == eReqMethod_Post ||
935 }
936 
937 
938 void CHttpRequest::x_AddCookieHeader(const CUrl& url, bool initial)
939 {
940  if ( !m_Session ) return;
941  string cookies = m_Session->x_GetCookies(url);
942  if ( !cookies.empty() || !initial ) {
944  }
945 }
946 
947 
948 // CConn_HttpStream callback for header parsing.
949 // user_data must contain SAdjustData*.
950 // See the explanation for callback sequence in sx_Adjust.
952  void* user_data,
953  int server_error)
954 {
955  if ( !user_data ) return eHTTP_HeaderError;
956 
957  SAdjustData* adj = reinterpret_cast<SAdjustData*>(user_data);
958 
959  CHttpRequest* req = adj->m_Request;
960  CRef<CHttpResponse> resp = req->m_Response;
961 
963  = dynamic_cast<CConn_HttpStream_Base*>(req->m_Stream.get());
964  _ASSERT(http);
965 
966  // Prevent collecting multiple headers on redirects.
967  CHttpHeaders::THeaders headers;
968  _ASSERT(http_header == http->GetHTTPHeader());
969  s_ParseHttpHeader(http->GetHTTPHeader(), headers);
970 
971  // Capture status code/text.
972  resp->x_Update(headers, http->GetStatusCode(), http->GetStatusText());
973 
974  // Always read response body - normal content or error.
975  return eHTTP_HeaderContinue;
976 }
977 
978 
979 // CConn_HttpStream callback for handling retries and redirects.
980 // Reset and re-fill headers on redirects (failure_count == 0).
981 // user_data must contain SAdjustData*.
982 //
983 // For HTTP streams, the callbacks are coming in the following order:
984 // 1. *while* establishing a connection with the specified HTTP server (or
985 // through the chain of redirected-to server(s), therein) sx_ParseHeader
986 // gets called for every HTTP response received from the tried HTTP
987 // server(s); and sx_Adjust gets called for every redirect (with
988 // failure_count == 0) or HTTP request failure (failure_count contains a
989 // positive connection attempt number);
990 // 2. *after* the entire HTTP response has been read out, sx_Adjust is called
991 // again with failure_count == -1 to request the next URL.
992 // NOTE that by this time CHttpRequest* may no longer be valid.
993 // For service streams:
994 // 1. *before* establishing HTTP connection, sx_Adjust is called with
995 // failure_count == -1 to request SConnNetInfo's setup for the 1st found
996 // HTTP server; then sx_ParseHeader and sx_Adjust are called the same way
997 // as for the HTTP streams above (in 1.) in the process of establishing
998 // the HTTP session; however, if the negotiation fails, sx_Adjust may be
999 // called for the next HTTP server for the service (w/failure_count == -1)
1000 // etc -- this process repeats until a satisfactory conneciton is made;
1001 // 2. once the HTTP data exchange has occurred, sx_Adjust is NOT called at
1002 // the end of the HTTP data stream.
1003 // Note that adj->m_Request can only be considered valid at either of the steps
1004 // number 1 above because those actions get performed in the valid CHttpRequest
1005 // context, namely from CHttpRequest::Execute(). Once that call is finished,
1006 // CHttpRequest may no longer be accessible.
1007 //
1009  void* user_data,
1010  unsigned int failure_count)
1011 {
1012  if ( !user_data ) return 0; // error, stop
1013 
1014  SAdjustData* adj = reinterpret_cast<SAdjustData*>(user_data);
1015  if (failure_count == (unsigned int)(-1) && !adj->m_IsService)
1016  return -1; // no new URL
1017 
1018  CHttpRequest* req = adj->m_Request;
1019  CRef<CHttpResponse> resp = req->m_Response;
1020 
1021  if (failure_count && failure_count != (unsigned int)(-1)) {
1022  // On the following errors do not retry, abort the request.
1023  switch ( resp->GetStatusCode() ) {
1024  case 400:
1025  case 403:
1026  case 404:
1027  case 405:
1028  case 406:
1029  case 410:
1030  return 0; // stop
1031  default:
1032  break;
1033  }
1034  if (!adj->m_IsService)
1035  return 1; // ok to retry
1036  }
1037  _ASSERT(!failure_count/*redirect*/ || adj->m_IsService);
1038 
1039  // Update location if it's different from the original URL.
1040  auto loc = make_c_unique(ConnNetInfo_URL(net_info));
1041  if (loc) {
1042  CUrl url(loc.get());
1043  if (failure_count) {
1044  _ASSERT(adj->m_IsService);
1045  bool adjust;
1046  if (req->m_AdjustUrl)
1047  adjust = req->m_AdjustUrl->AdjustUrl(url);
1048  else {
1049  url.Adjust(req->m_Url,
1053  adjust = true;
1054  }
1055  if ( adjust ) {
1056  // Re-read the url and save that in the response.
1057  string new_url = url.ComposeUrl(CUrlArgs::eAmp_Char);
1058  if (!ConnNetInfo_ParseURL(net_info, new_url.c_str())) {
1059  NCBI_THROW(CHttpSessionException, eConnFailed,
1060  "Cannot parse URL " + new_url);
1061  }
1062  if (!(loc = make_c_unique(ConnNetInfo_URL(net_info)))) {
1063  NCBI_THROW(CHttpSessionException, eConnFailed,
1064  "Cannot obtain updated URL");
1065  }
1066  }
1067  }
1068  resp->m_Location.SetUrl(loc.get());
1069  } else {
1070  NCBI_THROW(CHttpSessionException, eConnFailed,
1071  "Cannot obtain original URL");
1072  }
1073 
1074  // Discard old cookies, add those for the new location.
1075  req->x_AddCookieHeader(resp->m_Location, false);
1076  string headers = req->m_Headers->GetHttpHeader();
1077  if (!ConnNetInfo_OverrideUserHeader(net_info, headers.c_str())) {
1078  NCBI_THROW(CHttpSessionException, eConnFailed,
1079  "Cannot set HTTP header(s)");
1080  }
1081  return 1; // proceed
1082 }
1083 
1084 
1086 {
1087  m_Timeout = timeout;
1088  return *this;
1089 }
1090 
1091 
1093  unsigned int usec)
1094 {
1095  m_Timeout.Set(sec, usec);
1096  return *this;
1097 }
1098 
1099 
1101 {
1102  m_Deadline = deadline;
1103  return *this;
1104 }
1105 
1106 
1108 {
1109  m_RetryProcessing = on_off;
1110  return *this;
1111 }
1112 
1113 
1115 {
1116  m_Timeout = param.GetTimeout();
1117  m_Retries = param.GetRetries();
1118  m_Proxy = param.GetProxy();
1119  m_Deadline = param.GetDeadline();
1121  Headers().Merge(param.GetHeaders());
1125  }
1126 }
1127 
1128 
1129 ///////////////////////////////////////////////////////
1130 // CHttpSession_Base::
1131 //
1132 
1133 
1135  : m_Protocol(protocol),
1136  m_HttpFlags(0)
1137 {
1138 }
1139 
1140 
1142 {
1143  return CHttpRequest(*this, url, EReqMethod(method), param);
1144 }
1145 
1146 
1148  const CTimeout& timeout,
1149  THttpRetries retries)
1150 {
1151  CHttpRequest req = NewRequest(url, eGet);
1152  req.SetTimeout(timeout);
1153  req.SetRetries(retries);
1154  return req.Execute();
1155 }
1156 
1157 
1159  CTempString data,
1160  CTempString content_type,
1161  const CTimeout& timeout,
1162  THttpRetries retries)
1163 {
1164  CHttpRequest req = NewRequest(url, ePost);
1165  req.SetTimeout(timeout);
1166  req.SetRetries(retries);
1167  if ( content_type.empty() ) {
1168  content_type = kContentType_FormUrlEnc;
1169  }
1170  req.Headers().SetValue(CHttpHeaders::eContentType, content_type);
1171  if ( !data.empty() ) {
1172  req.ContentStream() << data;
1173  }
1174  return req.Execute();
1175 }
1176 
1177 
1179  CTempString data,
1180  CTempString content_type,
1181  const CTimeout& timeout,
1182  THttpRetries retries)
1183 {
1184  CHttpRequest req = NewRequest(url, ePut);
1185  req.SetTimeout(timeout);
1186  req.SetRetries(retries);
1187  if ( content_type.empty() ) {
1188  content_type = kContentType_FormUrlEnc;
1189  }
1190  req.Headers().SetValue(CHttpHeaders::eContentType, content_type);
1191  if ( !data.empty() ) {
1192  req.ContentStream() << data;
1193  }
1194  return req.Execute();
1195 }
1196 
1197 
1198 // Mutex protecting session cookies.
1200 
1201 
1203  const CUrl* url)
1204 {
1205  CFastMutexGuard lock(s_SessionMutex);
1206  ITERATE(CHttpHeaders::THeaderValues, it, cookies) {
1208  }
1209 }
1210 
1211 
1212 string CHttpSession_Base::x_GetCookies(const CUrl& url) const
1213 {
1214  string cookies;
1215  CFastMutexGuard lock(s_SessionMutex);
1216  CHttpCookie_CI it = m_Cookies.begin(url);
1217  for (; it; ++it) {
1218  if ( !cookies.empty() ) {
1219  cookies += "; ";
1220  }
1221  cookies += it->AsString(CHttpCookie::eHTTPRequest);
1222  }
1223  return cookies;
1224 }
1225 
1226 
1227 void CHttpSession_Base::SetCredentials(shared_ptr<CTlsCertCredentials> cred)
1228 {
1229  if (m_Credentials) {
1231  "Session credentials already set");
1232  }
1233  m_Credentials = cred;
1234 }
1235 
1236 
1238  : m_Headers(new CHttpHeaders),
1239  m_Timeout(CTimeout::eDefault),
1240  m_Deadline(CTimeout::eDefault),
1241  m_RetryProcessing(ESwitch::eDefault)
1242 {
1243 }
1244 
1245 
1247 {
1248  m_Headers->Assign(headers);
1249  return *this;
1250 }
1251 
1252 
1254 {
1255  m_Headers->SetValue(header, value);
1256  return *this;
1257 }
1258 
1259 
1261 {
1262  m_Headers->AddValue(header, value);
1263  return *this;
1264 }
1265 
1266 
1268 {
1269  m_Timeout = timeout;
1270  return *this;
1271 }
1272 
1273 
1275 {
1276  m_Retries = retries;
1277  return *this;
1278 }
1279 
1280 
1281 CHttpParam& CHttpParam::SetCredentials(shared_ptr<CTlsCertCredentials> credentials)
1282 {
1283  m_Credentials = credentials;
1284  return *this;
1285 }
1286 
1287 
1288 CHttpResponse g_HttpGet(const CUrl& url, const CHttpParam& param)
1289 {
1290  CRef<CHttpSession> session(new CHttpSession);
1291  session->SetCredentials(param.GetCredentials());
1292  CHttpRequest req = session->NewRequest(url, CHttpSession::eGet, param);
1293  return req.Execute();
1294 }
1295 
1296 
1298  const CTimeout& timeout,
1299  THttpRetries retries)
1300 {
1301  CHttpHeaders hdr;
1302  return g_HttpGet(url, hdr, timeout, retries);
1303 }
1304 
1305 
1307  const CHttpHeaders& headers,
1308  const CTimeout& timeout,
1309  THttpRetries retries)
1310 {
1311  CRef<CHttpSession> session(new CHttpSession);
1312  CHttpRequest req = session->NewRequest(url, CHttpSession::eGet);
1313  req.SetTimeout(timeout);
1314  req.SetRetries(retries);
1315  req.Headers().Merge(headers);
1316  return req.Execute();
1317 }
1318 
1319 
1321  CTempString data,
1322  const CHttpParam& param)
1323 {
1324  CRef<CHttpSession> session(new CHttpSession);
1325  session->SetCredentials(param.GetCredentials());
1326  CHttpRequest req = session->NewRequest(url, CHttpSession::ePost, param);
1327 
1331  }
1332 
1333  if (!data.empty()) {
1334  req.ContentStream() << data;
1335  }
1336 
1337  return req.Execute();
1338 }
1339 
1340 
1342  CTempString data,
1343  CTempString content_type,
1344  const CTimeout& timeout,
1345  THttpRetries retries)
1346 {
1347  CHttpHeaders hdr;
1348  return g_HttpPost(url, hdr, data, content_type, timeout, retries);
1349 }
1350 
1351 
1353  const CHttpHeaders& headers,
1354  CTempString data,
1355  CTempString content_type,
1356  const CTimeout& timeout,
1357  THttpRetries retries)
1358 {
1359  CRef<CHttpSession> session(new CHttpSession);
1360  CHttpRequest req = session->NewRequest(url, CHttpSession::ePost);
1361  req.SetTimeout(timeout);
1362  req.SetRetries(retries);
1363  req.Headers().Merge(headers);
1364 
1365  if ( content_type.empty() ) {
1366  if ( headers.HasValue(CHttpHeaders::eContentType) ) {
1369  }
1370  else {
1373  }
1374  }
1375  else {
1376  req.Headers().SetValue(CHttpHeaders::eContentType, content_type);
1377  }
1378 
1379  if ( !data.empty() ) {
1380  req.ContentStream() << data;
1381  }
1382 
1383  return req.Execute();
1384 }
1385 
1386 
1388  CTempString data,
1389  const CHttpParam& param)
1390 {
1391  CRef<CHttpSession> session(new CHttpSession);
1392  session->SetCredentials(param.GetCredentials());
1393  CHttpRequest req = session->NewRequest(url, CHttpSession::ePut, param);
1394 
1398  }
1399 
1400  if (!data.empty()) {
1401  req.ContentStream() << data;
1402  }
1403 
1404  return req.Execute();
1405 }
1406 
1407 
1409  CTempString data,
1410  CTempString content_type,
1411  const CTimeout& timeout,
1412  THttpRetries retries)
1413 {
1414  CHttpHeaders hdr;
1415  return g_HttpPut(url, hdr, data, content_type, timeout, retries);
1416 }
1417 
1418 
1420  const CHttpHeaders& headers,
1421  CTempString data,
1422  CTempString content_type,
1423  const CTimeout& timeout,
1424  THttpRetries retries)
1425 {
1426  CRef<CHttpSession> session(new CHttpSession);
1427  CHttpRequest req = session->NewRequest(url, CHttpSession::ePut);
1428  req.SetTimeout(timeout);
1429  req.SetRetries(retries);
1430  req.Headers().Merge(headers);
1431 
1432  if ( content_type.empty() ) {
1433  if ( headers.HasValue(CHttpHeaders::eContentType) ) {
1436  }
1437  else {
1440  }
1441  }
1442  else {
1443  req.Headers().SetValue(CHttpHeaders::eContentType, content_type);
1444  }
1445 
1446  if ( !data.empty() ) {
1447  req.ContentStream() << data;
1448  }
1449 
1450  return req.Execute();
1451 }
1452 
1453 
1454 ///////////////////////////////////////////////////////
1455 // CHttpSessionException::
1456 //
1457 
1458 
1460 {
1461  switch (GetErrCode()) {
1462  case eConnFailed: return "Connection failed";
1463  case eBadRequest: return "Bad request";
1464  case eBadContentType: return "Bad Content-Type";
1465  case eBadFormDataName: return "Bad form data name";
1466  case eBadFormData: return "Bad form data";
1467  case eBadStream: return "Bad stream";
1468  case eOther: return "Other error";
1469  default: return CException::GetErrCodeString();
1470  }
1471 }
1472 
1473 
#define false
Definition: bool.h:36
Helper base class for HTTP-like streams.
This stream exchanges data with an HTTP server located at the URL: http[s]://host[:port]/path[?...
This stream exchanges data with a named service, in a constraint that the service is implemented as o...
CDeadline.
Definition: ncbitime.hpp:1830
Primitive encoder - all methods return the argument value.
Definition: ncbi_url.hpp:91
CFileDataProvider(const string &file_name, const string &content_type)
virtual void WriteData(CNcbiOstream &out) const
Write user data to the stream.
virtual string GetFileName(void) const
Get optional filename to be shown in Content-Disposition header.
virtual ~CFileDataProvider(void)
virtual string GetContentType(void) const
Get content type.
CFile –.
Definition: ncbifile.hpp:1604
virtual string EncodeArgName(const string &name) const
Encode URL argument name.
virtual string EncodeArgValue(const string &value) const
Encode URL argument value.
Interface for custom form data providers.
CHttpCookie_CI::
POST request data.
Helper class allowing to use both strings and enums as header names.
CHttpSession and CHttpRequest parameters.
Per-request proxy settings.
HTTP request.
HTTP response.
CHttpSessionException –.
HTTP session class, holding common data for multiple requests.
CSafeStatic<>::
CStringException –.
Definition: ncbistr.hpp:4505
CTempString implements a light-weight string on top of a storage buffer whose lifetime management is ...
Definition: tempstr.hpp:65
CTimeException –.
Definition: ncbitime.hpp:2076
CTimeout – Timeout interval.
Definition: ncbitime.hpp:1693
CUrlArgs::
Definition: ncbi_url.hpp:240
CUrl –.
Definition: ncbi_url.hpp:353
container_type::const_iterator const_iterator
Definition: map.hpp:53
const_iterator end() const
Definition: map.hpp:152
bool empty() const
Definition: map.hpp:149
void clear()
Definition: map.hpp:169
const_iterator find(const key_type &key) const
Definition: map.hpp:153
void swap(this_type &m)
Definition: map.hpp:118
char value[7]
Definition: config.c:431
The NCBI C++ standard methods for dealing with std::string.
const char * file_name[]
static DLIST_TYPE *DLIST_NAME() last(DLIST_LIST_TYPE *list)
Definition: dlist.tmpl.h:51
std::ofstream out("events_result.xml")
main entry point for tests
bool IsNull(void) const
Check if the object is unassigned.
Definition: ncbimisc.hpp:686
#define ITERATE(Type, Var, Cont)
ITERATE macro to sequence through container elements.
Definition: ncbimisc.hpp:815
unique_ptr< T, TDeleter > make_c_unique(T *p, TDeleter d)
Eliminates the necessity for specifying types of both allocated resource and deallocating C function.
Definition: ncbimisc.hpp:1448
@ eDefault
Definition: ncbi_types.h:112
@ eOn
Definition: ncbi_types.h:111
int GetStatusCode(void) const
Get the last seen HTTP status code, if available.
const CTempString GetStatusText(void) const
Get the last seen HTTP status text, if available.
const string & GetHTTPHeader(void) const
Get the last seen HTTP header text, if available.
FHTTP_ParseHeader parse_header
EHTTP_HeaderParse
The extended version HTTP_CreateConnectorEx() is able to track the HTTP response chain and also chang...
FSERVICE_Cleanup cleanup
@ fHTTP_AdjustOnRedirect
Call adjust routine for redirects, too.
@ eHTTP_HeaderError
Parse failed, treat as a server error.
@ eHTTP_HeaderContinue
Parse succeeded, continue with body.
#define _VERIFY(expr)
Definition: ncbidbg.hpp:161
#define ERR_POST(message)
Error posting with file, line number information but without error codes.
Definition: ncbidiag.hpp:186
TErrCode GetErrCode(void) const
Get error code.
Definition: ncbiexpt.cpp:453
#define NCBI_THROW(exception_class, err_code, message)
Generic macro to throw an exception, given the exception class, error code and message string.
Definition: ncbiexpt.hpp:704
TErrCode GetErrCode(void) const
Definition: ncbiexpt.hpp:1493
virtual const char * GetErrCodeString(void) const
Get error code interpreted as text.
Definition: ncbiexpt.cpp:444
const_iterator begin(void) const
Iterate all cookies.
string AsString(ECookieFormat format) const
Compose string from the cookie.
void Add(const CHttpCookie &cookie)
Add a single cookie.
shared_ptr< iostream > m_Stream
void ParseHttpHeader(const CTempString &headers)
Parse headers from the string (usually provided by a stream callback).
void SetRetries(THttpRetries retries)
Set number of retries.
static const char * GetHeaderName(EHeaderName name)
Get string representation of the given name.
void x_InitConnection2(shared_ptr< iostream > stream)
vector< SFormData > TValues
CHttpResponse Get(const CUrl &url, const CTimeout &timeout=CTimeout(CTimeout::eDefault), THttpRetries retries=null)
Shortcut for GET requests.
shared_ptr< CTlsCertCredentials > GetCredentials(void) const
THttpRetries GetRetries(void) const
CHttpRequest & SetTimeout(const CTimeout &timeout)
Set new timeout.
EHeaderName
Some standard HTTP headers.
EProtocol GetProtocol(void) const
Get protocol version.
const CHttpProxy & GetProxy(void) const
void AddEntry(CTempString entry_name, CTempString value, CTempString content_type=CTempString())
Add name=value pair.
CHttpParam & AddHeader(CHttpHeaders::EHeaderName header, CTempString value)
Add a single HTTP header,.
CHttpParam & SetRetries(THttpRetries retries)
CNcbiIstream & ContentStream(void) const
Get input stream.
CRef< CAdjustUrlCallback_Base > m_AdjustUrl
const CTimeout & GetDeadline() const
const string & GetHost(void) const
ESwitch GetRetryProcessing() const
CRef< CHttpHeaders > m_Headers
void x_Update(CHttpHeaders::THeaders headers, int status_code, string status_text)
int GetStatusCode(void) const
Get response status code.
CHttpParam & SetHeaders(const CHttpHeaders &headers)
Add all HTTP headers to request.
void AddProvider(CTempString entry_name, CFormDataProvider_Base *provider)
Add custom data provider.
CRef< CHttpSession_Base > m_Session
void SetValue(CHeaderNameConverter name, CTempString value)
Remove all existing values with the name, set the new value.
unsigned short GetPort(void) const
CRef< CHttpSession_Base > m_Session
CHttpRequest & SetRetryProcessing(ESwitch on_off)
Set whether Execute() should wait for actual response.
CHttpSession_Base(EProtocol protocol)
bool x_CanSendData(void) const
const THeaderValues & GetAllValues(CHeaderNameConverter name) const
Get all values with the given name.
void WriteFormData(CNcbiOstream &out) const
Write form data to the stream using the selected form content type.
void x_SetProxy(SConnNetInfo &net_info)
const CHttpHeaders & Headers(void) const
Get incoming HTTP headers.
void x_InitConnection(bool use_form_data)
CNcbiIstream & ErrorStream(void) const
Get input stream containing error message (e.g.
string GetHttpHeader(void) const
Get all headers as a single string as required by CConn_HttpStream.
const CTimeout & GetTimeout(void) const
const string & GetPassword(void) const
string x_GetCookies(const CUrl &url) const
static int sx_Adjust(SConnNetInfo *net_info, void *user_data, unsigned int failure_count)
void ClearAll(void)
Remove all headers.
CHttpParam & SetCredentials(shared_ptr< CTlsCertCredentials > credentials)
CHttpRequest & SetDeadline(const CTimeout &deadline)
Set new deadline for Execute().
CNcbiOstream & ContentStream(void)
Get output stream to write user data.
void SetContentType(EContentType content_type)
Set content type for the form data.
void AddFile(CTempString entry_name, CTempString file_name, CTempString content_type=CTempString())
Add file entry.
NCBI_CRED GetNcbiCred(void) const
void Clear(void)
Clear all form data, reset content type to the default eFormUrlEncoded.
virtual const char * GetErrCodeString(void) const override
Get error code interpreted as text.
EContentType
Supported content types for POST requests.
shared_ptr< iostream > m_Stream
void AddValue(CHeaderNameConverter name, CTempString value)
Add new value with the name (multiple values are allowed with the same name, the order is preserved).
bool CanGetContentStream(void) const
Check if the requested content can be read from the content stream.
vector< CRef< CFormDataProvider_Base > > TProviders
void Clear(CHeaderNameConverter name)
Remove all values with the given name.
void x_UpdateResponse(CHttpHeaders::THeaders headers, int status_code, string status_text)
static EHTTP_HeaderParse sx_ParseHeader(const char *headers, void *user_data, int server_error)
static string CreateBoundary(void)
Generate a new random string to be used as multipart boundary.
CRef< CHttpHeaders > m_Headers
bool x_IsReservedHeader(CTempString name) const
const CHttpHeaders & GetHeaders(void) const
CHttpHeaders & Headers(void)
Get HTTP headers to be sent.
CRef< CHttpFormData > m_FormData
shared_ptr< CTlsCertCredentials > m_Credentials
CHttpResponse g_HttpPost(const CUrl &url, CTempString data, const CHttpParam &param)
Shortcut for POST request.
const CHttpProxy & GetProxy(void) const
bool IsEmpty(void) const
Check if the form data is empty (no entries have been added).
CHttpResponse Post(const CUrl &url, CTempString data, CTempString content_type=CTempString(), const CTimeout &timeout=CTimeout(CTimeout::eDefault), THttpRetries retries=null)
Shortcut for POST requests.
void x_AdjustHeaders(bool use_form_data)
void x_AddCookieHeader(const CUrl &url, bool initial)
virtual bool x_Downgrade(CHttpResponse &resp, EProtocol &protocol) const =0
TProviderEntries m_Providers
void Merge(const CHttpHeaders &headers)
Add values from 'headers' to this object.
CHttpParam & SetTimeout(const CTimeout &timeout)
CHttpResponse Put(const CUrl &url, CTempString data, CTempString content_type=CTempString(), const CTimeout &timeout=CTimeout(CTimeout::eDefault), THttpRetries retries=null)
Shortcut for PUT requests.
CHttpResponse(CHttpSession_Base &session, const CUrl &url, shared_ptr< iostream > stream={})
EContentType m_ContentType
THTTP_Flags GetHttpFlags(void) const
Get flags passed to CConn_HttpStream.
CHttpResponse g_HttpPut(const CUrl &url, CTempString data, const CHttpParam &param)
Shortcut for PUT request.
const CHttpProxy & GetProxy(void) const
unsigned short operator()(void) const
void x_SetCookies(const CHttpHeaders::THeaderValues &cookies, const CUrl *url)
shared_ptr< CTlsCertCredentials > m_Credentials
const string & GetValue(CHeaderNameConverter name) const
Get value of the header or empty string.
shared_ptr< CTlsCertCredentials > m_Credentials
CHttpRequest NewRequest(const CUrl &url, ERequestMethod method=eGet, const CHttpParam &param={})
Initialize request.
virtual void x_StartRequest(EProtocol protocol, CHttpRequest &req, bool use_form_data)=0
CHttpResponse Execute(void)
Send the request, initialize and return the response.
ERequestMethod
Supported request methods, proxy for EReqMethod.
void SetCredentials(shared_ptr< CTlsCertCredentials > cred)
Set TLS credentials.
CRef< CHttpResponse > m_Response
CHttpResponse g_HttpGet(const CUrl &url, const CHttpParam &param)
Shortcut for GET request.
void SetParam(const CHttpParam &param)
Set request parameters.
CTlsCertCredentials(const CTempStringEx &cert, const CTempStringEx &pkey)
Initialize credentials.
size_t CountValues(CHeaderNameConverter name) const
Get number of values with the given name.
bool HasValue(CHeaderNameConverter name) const
Check if there's at least one header with the name.
THttpRetries m_Retries
const string & GetUser(void) const
string GetContentTypeStr(void) const
Get the form content type as a string.
bool IsEmpty(void) const
CRef< CHttpHeaders > m_Headers
vector< string > THeaderValues
List of header values (may be required for headers with multiple values like Cookie).
CHttpFormData & FormData(void)
Get form data to be sent with POST request.
void Assign(const CHttpHeaders &headers)
Clear any existing values and copy all headers from 'headers' to this object.
EProtocol
HTTP protocol version.
THttpRetries m_Retries
CHttpParam & SetHeader(CHttpHeaders::EHeaderName header, CTempString value)
Set or replace a single HTTP header,.
@ eFormUrlEncoded
'application/x-www-form-urlencoded', default
@ eMultipartFormData
'multipart/form-data'
@ eBadStream
Wrong stream used to read content or error.
@ eBadFormData
Bad form data (e.g. unreadable file).
@ eBadContentType
Content-type conflicts with the data.
@ eBadFormDataName
Empty or bad name in form data.
@ eBadRequest
Error initializing or sending a request.
@ eConnFailed
Failed to open connection.
CRef< C > Ref(C *object)
Helper functions to get CRef<> and CConstRef<> objects.
Definition: ncbiobj.hpp:2015
void Reset(void)
Reset reference object.
Definition: ncbiobj.hpp:773
bool Empty(void) const THROWS_NONE
Check if CRef is empty – not pointing to any object, which means having a null value.
Definition: ncbiobj.hpp:719
int64_t Int8
8-byte (64-bit) signed integer
Definition: ncbitype.h:104
#define END_NCBI_SCOPE
End previously defined NCBI scope.
Definition: ncbistl.hpp:103
#define BEGIN_NCBI_SCOPE
Define ncbi namespace.
Definition: ncbistl.hpp:100
#define NCBI_AS_STRING(value)
Convert some value to string even if this value is macro itself.
Definition: ncbistl.hpp:146
@ fSERV_Http
NCBI_CRED NcbiCreateTlsCertCredentials(const void *cert, size_t certsz, const void *pkey, size_t pkeysz)
Build NCBI_CRED from memory buffers containing X.509 certificate and private key, respectively,...
Definition: ncbi_tls.c:120
void NcbiDeleteTlsCertCredentials(NCBI_CRED cred)
Delete a NCBI_CRED handle created by NcbiCreateTlsCertCredentials().
Definition: ncbi_tls.c:180
void NcbiStreamCopyThrow(CNcbiOstream &os, CNcbiIstream &is)
Same as NcbiStreamCopy() but throws an CCoreException when copy fails.
Definition: ncbistre.cpp:225
IO_PREFIX::ostream CNcbiOstream
Portable alias for ostream.
Definition: ncbistre.hpp:149
IO_PREFIX::istream CNcbiIstream
Portable alias for istream.
Definition: ncbistre.hpp:146
IO_PREFIX::ifstream CNcbiIfstream
Portable alias for ifstream.
Definition: ncbistre.hpp:439
#define kEmptyStr
Definition: ncbistr.hpp:123
static int CompareNocase(const CTempString s1, SIZE_TYPE pos, SIZE_TYPE n, const char *s2)
Case-insensitive compare of a substring with another string.
Definition: ncbistr.cpp:219
static list< string > & Split(const CTempString str, const CTempString delim, list< string > &arr, TSplitFlags flags=0, vector< SIZE_TYPE > *token_pos=NULL)
Split a string using specified delimiters.
Definition: ncbistr.cpp:3457
#define NPOS
Definition: ncbistr.hpp:133
static void TruncateSpacesInPlace(string &str, ETrunc where=eTrunc_Both)
Truncate spaces in a string (in-place)
Definition: ncbistr.cpp:3197
static unsigned long StringToULong(const CTempString str, TStringToNumFlags flags=0, int base=10)
Convert string to unsigned long.
Definition: ncbistr.cpp:665
bool empty(void) const
Return true if the represented string is empty (i.e., the length is zero)
Definition: tempstr.hpp:334
static bool EqualNocase(const CTempString s1, SIZE_TYPE pos, SIZE_TYPE n, const char *s2)
Case-insensitive equality of a substring with another string.
Definition: ncbistr.hpp:5352
bool HasZeroAtEnd(void) const
Definition: tempstr.hpp:1036
TErrCode GetErrCode(void) const
Get error code.
Definition: ncbistr.hpp:4454
static enable_if< is_arithmetic< TNumeric >::value||is_convertible< TNumeric, Int8 >::value, string >::type NumericToString(TNumeric value, TNumToStringFlags flags=0, int base=10)
Convert numeric value to string.
Definition: ncbistr.hpp:673
static string URLEncode(const CTempString str, EUrlEncode flag=eUrlEnc_SkipMarkChars)
URL-encode string.
Definition: ncbistr.cpp:6058
@ fSplit_Truncate
Definition: ncbistr.hpp:2501
@ fSplit_MergeDelimiters
Merge adjacent delimiters.
Definition: ncbistr.hpp:2498
@ eUrlEnc_URIQueryValue
Encode query part of an URI, arg value.
Definition: ncbistr.hpp:3151
@ eUrlEnc_URIQueryName
Encode query part of an URI, arg name.
Definition: ncbistr.hpp:3150
@ eConvert
Failure to convert string.
Definition: ncbistr.hpp:4509
@ eTrunc_Both
Truncate spaces at both begin and end of string.
Definition: ncbistr.hpp:2242
CNanoTimeout GetRemainingTime(void) const
Get time left to the expiration.
Definition: ncbitime.cpp:3858
bool IsExpired(void) const
Check if the deadline is expired.
Definition: ncbitime.hpp:1856
const long kMilliSecondsPerSecond
Number milliseconds in one second.
Definition: ncbitime.hpp:96
unsigned long GetAsMilliSeconds(void) const
Get as number of milliseconds.
Definition: ncbitime.cpp:3489
bool IsDefault() const
Definition: ncbitime.hpp:2724
void Set(EType type)
Set special value.
Definition: ncbitime.cpp:3576
@ eConvert
Error converting value from one format to another.
Definition: ncbitime.hpp:2081
string GetQueryString(EAmpEncoding amp_enc, NStr::EUrlEncode encode) const
Construct and return complete query string.
Definition: ncbi_url.cpp:225
const string & GetService(void) const
Definition: ncbi_url.hpp:415
void Adjust(const CUrl &other, TAdjustFlags flags)
Adjust this URL using information from 'other' URL.
Definition: ncbi_url.cpp:672
const string & GetScheme(void) const
Definition: ncbi_url.hpp:398
void SetUrl(const string &url, const IUrlEncoder *encoder=0)
Parse the URL.
Definition: ncbi_url.cpp:409
void SetValue(const string &name, const string &value)
Set new value for the first argument with the given name or add a new argument.
Definition: ncbi_url.cpp:270
bool IsService(void) const
Definition: ncbi_url.hpp:414
string ComposeUrl(CUrlArgs::EAmpEncoding amp_enc, const IUrlEncoder *encoder=0) const
Compose the URL.
Definition: ncbi_url.cpp:568
@ eAmp_Char
Use & to separate arguments.
Definition: ncbi_url.hpp:254
@ fScheme_Replace
Replace scheme if set in 'other'.
Definition: ncbi_url.hpp:465
@ fPath_Append
Append new path to the existing one.
Definition: ncbi_url.hpp:458
@ fArgs_Merge
Append new args; replace values of existing args, do not allow to set multiple values with the same n...
Definition: ncbi_url.hpp:463
char http_proxy_host[255+1]
enum ENcbiSwitch ESwitch
Aux.
char http_proxy_user[63+1]
unsigned short http_proxy_port
int ConnNetInfo_SetTimeout(SConnNetInfo *net_info, const STimeout *timeout)
int ConnNetInfo_OverrideUserHeader(SConnNetInfo *net_info, const char *header)
const STimeout * g_CTimeoutToSTimeout(const CTimeout &cto, STimeout &sto)
CTimeout/STimeout adapters.
int ConnNetInfo_ParseURL(SConnNetInfo *net_info, const char *url)
char http_proxy_pass[63+1]
EReqMethod
char * ConnNetInfo_URL(const SConnNetInfo *net_info)
#define REG_CONN_MAX_TRY
SConnNetInfo * ConnNetInfo_Create(const char *service)
#define DEF_CONN_MAX_TRY
void ConnNetInfo_Destroy(SConnNetInfo *net_info)
@ eURL_Http
@ eURL_Https
@ eReqMethod_Put
@ eReqMethod_Patch
@ eReqMethod_Get
@ eReqMethod_Post
char * buf
int i
range(_Ty, _Ty) -> range< _Ty >
const char * ConnNetInfo_GetValueInternal(const char *service, const char *param, char *value, size_t value_size, const char *def_value)
#define CONN_HOST_LEN
Definition: ncbi_connutil.h:99
#define CONN_USER_LEN
Definition: ncbi_connutil.h:97
#define CONN_PASS_LEN
Definition: ncbi_connutil.h:98
static const char * kReservedHeaders[]
const char * kContentType_MultipartFormData
static const char kHttpHeaderDelimiter
DEFINE_STATIC_FAST_MUTEX(s_SessionMutex)
const char * kContentType_FormUrlEnc
static Int8 s_SimpleRand(Int8 range)
static void x_WritePartHeader(CNcbiOstream &out, const string &boundary, const string &name, const string &content_type, const string &filename=kEmptyStr)
static void s_Cleanup(void *user_data)
static const char * kHttpHeaderNames[]
static void s_ParseHttpHeader(const CTempString &from, CHttpHeaders::THeaders &to)
unsigned short x_RetriesToMaxtry(unsigned short retries)
static CSafeStatic< CHttpHeaders::THeaderValues > kEmptyValues
unsigned int a
Definition: ncbi_localip.c:102
void SleepMilliSec(unsigned long ml_sec, EInterruptOnSignal onsignal=eRestartOnSignal)
Defines classes: CDirEntry, CFile, CDir, CSymLink, CMemoryFile, CFileUtil, CFileLock,...
Multi-threading – mutexes; rw-locks; semaphore.
#define HTTP_EOL
Definition: ncbistre.hpp:120
std::istream & in(std::istream &in_, double &x_)
double f(double x_, const double &y_)
Definition: njn_root.hpp:188
Defines CRequestContext class for NCBI C++ diagnostic API.
SAdjustData(CHttpRequest *request=0)
CHttpRequest * m_Request
bool operator()(const CHttpHeaders &headers)
SValueRestorer< CRef< CHttpHeaders >, CHttpHeaders > m_Headers
static void Assign(TTo &, const TFrom &)
SRetryProcessing(ESwitch on_off, const CTimeout &deadline, CUrl &url, EReqMethod &method, CRef< CHttpHeaders > &headers, CRef< CHttpFormData > &form_data)
SValueRestorer< CUrl > m_Url
SValueRestorer< CRef< CHttpFormData > > m_FormData
SValueRestorer< EReqMethod > m_Method
Timeout structure.
Definition: ncbi_types.h:76
#define _ASSERT
Modified on Fri Dec 01 04:43:34 2023 by modify_doxy.py rev. 669887