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 102055 2024-03-23 01:54:52Z 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  // Prevent collecting multiple headers on redirects.
513  m_Headers->m_Headers.swap(headers);
514  m_StatusCode = status_code;
515  m_StatusText = std::move(status_text);
516  const auto& cookies = m_Headers->GetAllValues(CHttpHeaders::eSetCookie);
517  m_Session->x_SetCookies(cookies, &m_Location);
518 }
519 
520 
521 ///////////////////////////////////////////////////////
522 // CTlsCertCredentials::
523 //
524 
525 
527  : m_Cert(cert), m_PKey(pkey)
528 {
529  if (cert.HasZeroAtEnd()) m_Cert.push_back('\0');
530  if (pkey.HasZeroAtEnd()) m_PKey.push_back('\0');
531 }
532 
534 {
535  if ( m_Cred ) {
537  }
538 }
539 
540 
542 {
543  if ( !m_Cred ) {
544  m_Cred = NcbiCreateTlsCertCredentials(m_Cert.data(), m_Cert.size(), m_PKey.data(), m_PKey.size());
545  }
546  return m_Cred;
547 }
548 
549 
550 ///////////////////////////////////////////////////////
551 // CHttpRequest::
552 //
553 
554 
555 unsigned short SGetHttpDefaultRetries::operator()(void) const
556 {
557  char buf[16];
560  int maxtry = atoi(buf);
561  return (unsigned short)(maxtry ? maxtry - 1 : 0);
562 }
563 
564 
565 inline
566 unsigned short x_RetriesToMaxtry(unsigned short retries)
567 {
568  return (unsigned short)(++retries ? retries : retries - 1);
569 }
570 
571 
573  const CUrl& url,
574  EReqMethod method,
575  const CHttpParam& param)
576  : m_Session(&session),
577  m_Url(url),
578  m_Method(method),
579  m_Headers(new CHttpHeaders),
580  m_AdjustUrl(0),
581  m_Credentials(session.GetCredentials())
582 {
583  SetParam(param);
584 }
585 
586 
587 // Processing logic for 'retry later' response in CHttpRequest::Execute()
589 {
590  SRetryProcessing(ESwitch on_off, const CTimeout& deadline, CUrl& url,
591  EReqMethod& method, CRef<CHttpHeaders>& headers,
592  CRef<CHttpFormData>& form_data);
593 
594  bool operator()(const CHttpHeaders& headers);
595 
596 private:
597  // This class is used to restore CHttpRequest members to their original state
598  template <class TMember, class TValue = TMember>
600  {
601  TMember& value;
602 
603  SValueRestorer(TMember& v) : value(v) { Assign(original, value); }
605  void Restore() { Assign(value, original); }
606 
607  private:
608  TValue original;
609  };
610 
611  template <class TTo, class TFrom> static void Assign(TTo&, const TFrom&);
612 
613  const bool m_Enabled;
619 };
620 
621 
623  EReqMethod& method, CRef<CHttpHeaders>& headers,
624  CRef<CHttpFormData>& form_data) :
625  m_Enabled(on_off == eOn),
626  m_Deadline(deadline.IsDefault() ? CTimeout::eInfinite : deadline),
627  m_Url(url),
628  m_Method(method),
629  m_Headers(headers),
630  m_FormData(form_data)
631 {
632 }
633 
634 
636 {
637  // Must correspond to CHttpRetryContext (e.g. CCgi2RCgiApp) values
638  const unsigned long kExecuteDefaultRefreshDelay = 5;
639  const string kExecuteHeaderRetryURL = "X-NCBI-Retry-URL";
640  const string kExecuteHeaderRetryDelay = "X-NCBI-Retry-Delay";
641 
642  if (!m_Enabled) return false;
643  if (m_Deadline.IsExpired()) return false;
644 
645  const auto& retry_url = headers.GetValue(kExecuteHeaderRetryURL);
646 
647  // Not a 'retry later' response (e.g. remote CGI has already finished)
648  if (retry_url.empty()) return false;
649 
650  const auto& retry_delay = headers.GetValue(kExecuteHeaderRetryDelay);
651  unsigned long sleep_ms = kExecuteDefaultRefreshDelay;
652 
653  // HTTP header has delay
654  if (!retry_delay.empty()) {
655  try {
656  sleep_ms = NStr::StringToULong(retry_delay) * kMilliSecondsPerSecond;
657  }
658  catch (CStringException& ex) {
659  if (ex.GetErrCode() != CStringException::eConvert) throw;
660  }
661  }
662 
663  // If the deadline is less than the retry delay,
664  // sleep for the remaining and check once again
665  try {
666  auto remaining = m_Deadline.GetRemainingTime().GetAsMilliSeconds();
667  if (remaining < sleep_ms) sleep_ms = remaining;
668  }
669  catch (CTimeException& ex) {
670  if (ex.GetErrCode() != CTimeException::eConvert) throw;
671  }
672 
673  SleepMilliSec(sleep_ms);
674 
675  // Make subsequent requests appropriate
676  m_Url.value = retry_url;
678  m_Headers.Restore();
680 
681  return true;
682 }
683 
684 
685 template <class TTo, class TFrom>
686 void SRetryProcessing::Assign(TTo& to, const TFrom& from)
687 {
688  to = from;
689 }
690 
691 
692 template <>
694 {
695  to.Assign(*from);
696 }
697 
698 
699 template <>
701 {
702  to->Assign(from);
703 }
704 
705 
707 {
711  auto protocol = m_Session->GetProtocol();
712 
713  do {
714  // Connection not open yet.
715  // Only POST and PUT support sending form data.
716  bool have_data = m_FormData && !m_FormData.Empty();
717  if ( !m_Response ) {
718  if (m_Stream) {
719  NCBI_THROW(CHttpSessionException, eBadRequest,
720  "Attempt to execute HTTP request already being executed");
721  }
722 
723  m_Session->x_StartRequest(protocol, *this, have_data);
724  }
726  _ASSERT(m_Stream);
727  if ( have_data ) {
729  }
730  // Send data to the server and close output stream.
731  m_Stream->peek();
732  m_Stream.reset();
733  ret = m_Response;
734  m_Response.Reset();
735  }
736  while (m_Session->x_Downgrade(*ret, protocol) || retry_processing(ret->Headers()));
737 
738  return *ret;
739 }
740 
741 
743 {
744  if ( !x_CanSendData() ) {
745  NCBI_THROW(CHttpSessionException, eBadRequest,
746  "Request method does not allow writing to the output stream");
747  }
748  if ( !m_Stream ) {
749  if (m_Response) {
750  NCBI_THROW(CHttpSessionException, eBadRequest,
751  "Attempt to execute HTTP request already being executed");
752  }
753 
754  m_Session->x_StartRequest(m_Session->GetProtocol(), *this, false);
755  }
757  _ASSERT(m_Stream);
758  return *m_Stream;
759 }
760 
761 
763 {
764  if ( !x_CanSendData() ) {
765  NCBI_THROW(CHttpSessionException, eBadRequest,
766  "Request method does not support sending data");
767  }
768  if ( m_Stream ) {
769  NCBI_THROW(CHttpSessionException, eBadRequest,
770  "Can not get form data while executing request");
771  }
772  if ( !m_FormData ) {
774  }
775  return *m_FormData;
776 }
777 
778 
779 void CHttpRequest::x_AdjustHeaders(bool use_form_data)
780 {
781  x_AddCookieHeader(m_Url, true);
782  if (use_form_data) {
785  }
786 }
787 
788 
789 void CHttpRequest::x_UpdateResponse(CHttpHeaders::THeaders headers, int status_code, string status_text)
790 {
791  if (m_Response) {
792  m_Response->x_Update(std::move(headers), status_code, std::move(status_text));
793  }
794 }
795 
796 
798 {
799  CHttpProxy proxy = GetProxy();
800  // Try per-session proxy, if any.
801  if ( proxy.IsEmpty() ) proxy = m_Session->GetProxy();
802  if ( proxy.IsEmpty() ) return;
803 
804  if (proxy.GetHost().size() > CONN_HOST_LEN) {
805  NCBI_THROW(CHttpSessionException, eConnFailed,
806  "Proxy host length exceeds " NCBI_AS_STRING(CONN_HOST_LEN));
807  }
808  memcpy(net_info.http_proxy_host, proxy.GetHost().c_str(), proxy.GetHost().size() + 1);
809  net_info.http_proxy_port = proxy.GetPort();
810 
811  if (proxy.GetUser().size() > CONN_USER_LEN) {
812  NCBI_THROW(CHttpSessionException, eConnFailed,
813  "Proxy user length exceeds " NCBI_AS_STRING(CONN_USER_LEN));
814  }
815  memcpy(net_info.http_proxy_user, proxy.GetUser().c_str(), proxy.GetUser().size() + 1);
816 
817  if (proxy.GetPassword().size() > CONN_PASS_LEN) {
818  NCBI_THROW(CHttpSessionException, eConnFailed,
819  "Proxy password length exceeds " NCBI_AS_STRING(CONN_PASS_LEN));
820  }
821  memcpy(net_info.http_proxy_pass, proxy.GetPassword().c_str(), proxy.GetPassword().size() + 1);
822 }
823 
824 
825 // Interface for the HTTP connector's adjust callback
826 struct SAdjustData {
827  CHttpRequest* m_Request; // NB: don't use after request has been sent!
828  const bool m_IsService;
829 
830  SAdjustData(CHttpRequest* request, bool is_service)
831  : m_Request(request), m_IsService(is_service)
832  { }
833 };
834 
835 
836 static void s_Cleanup(void* user_data)
837 {
838  SAdjustData* adjust_data = reinterpret_cast<SAdjustData*>(user_data);
839  delete adjust_data;
840 }
841 
842 
843 void CHttpRequest::x_InitConnection(bool use_form_data)
844 {
845  bool is_service = m_Url.IsService();
846  unique_ptr<SConnNetInfo, void (*)(SConnNetInfo*)> net_info
847  (ConnNetInfo_Create(is_service ? m_Url.GetService().c_str() : 0),
849  if (!net_info || (is_service && !net_info->svc[0])) {
850  NCBI_THROW(CHttpSessionException, eConnFailed,
851  "Failed to create SConnNetInfo");
852  }
854  net_info->http_version = 1;
855  }
856  net_info->req_method = m_Method;
857 
858  // Set scheme if given in URL (only if http(s) since this is CHttpRequest).
859  string url_scheme(m_Url.GetScheme());
860  if (NStr::EqualNocase(url_scheme, "https")) {
861  net_info->scheme = eURL_Https;
862  }
863  else if (NStr::EqualNocase(url_scheme, "http")) {
864  net_info->scheme = eURL_Http;
865  }
866 
867  // Save headers set automatically (e.g. from CONN_HTTP_USER_HEADER).
868  if (net_info->http_user_header) {
869  s_ParseHttpHeader(net_info->http_user_header, m_Headers->m_Headers);
870  }
871 
872  x_AdjustHeaders(use_form_data);
873  string headers = m_Headers->GetHttpHeader();
874 
875  if ( !m_Timeout.IsDefault() ) {
876  STimeout sto;
878  }
879  if ( !m_Retries.IsNull() ) {
880  net_info->max_try = x_RetriesToMaxtry(m_Retries);
881  }
882  if ( m_Credentials ) {
883  net_info->credentials = m_Credentials->GetNcbiCred();
884  }
885  x_SetProxy(*net_info);
886 
888  unique_ptr<SAdjustData> adjust_data(new SAdjustData(this, is_service));
889  if ( !is_service ) {
890  // Connect using HTTP.
891  m_Stream.reset(new CConn_HttpStream(
893  net_info.get(),
894  headers.c_str(),
896  adjust_data.get(),
897  sx_Adjust,
898  s_Cleanup,
899  // Always set AdjustOnRedirect flag - to send correct cookies.
901  }
902  else {
903  // Try to resolve service name.
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  CHttpHeaders::THeaders headers;
967  _ASSERT(http_header == http->GetHTTPHeader());
968  s_ParseHttpHeader(http->GetHTTPHeader(), headers);
969 
970  // Capture status code/text.
971  resp->x_Update(headers, http->GetStatusCode(), http->GetStatusText());
972 
973  // Always read response body - normal content or error.
974  return eHTTP_HeaderContinue;
975 }
976 
977 
978 // CConn_HttpStream callback for handling retries and redirects.
979 // Reset and re-fill headers on redirects (failure_count == 0).
980 // user_data must contain SAdjustData*.
981 //
982 // For HTTP streams, the callbacks are coming in the following order:
983 // 1. *while* establishing a connection with the specified HTTP server (or
984 // through the chain of redirected-to server(s), therein) sx_ParseHeader
985 // gets called for every HTTP response received from the tried HTTP
986 // server(s); and sx_Adjust gets called for every redirect (with
987 // failure_count == 0) or HTTP request failure (failure_count contains a
988 // positive connection attempt number);
989 // 2. *after* the entire HTTP response has been read out, sx_Adjust is called
990 // again with failure_count == -1 to request the next URL.
991 // NOTE that by this time CHttpRequest* may no longer be valid.
992 // For service streams:
993 // 1. *before* establishing HTTP connection, sx_Adjust is called with
994 // failure_count == -1 to request SConnNetInfo's setup for the 1st found
995 // HTTP server; then sx_ParseHeader and sx_Adjust are called the same way
996 // as for the HTTP streams above (in 1.) in the process of establishing
997 // the HTTP session; however, if the negotiation fails, sx_Adjust may be
998 // called for the next HTTP server for the service (w/failure_count == -1)
999 // etc -- this process repeats until a satisfactory conneciton is made;
1000 // 2. once the HTTP data exchange has occurred, sx_Adjust is NOT called at
1001 // the end of the HTTP data stream.
1002 // Note that adj->m_Request can only be considered valid at either of the steps
1003 // number 1 above because those actions get performed in the valid CHttpRequest
1004 // context, namely from CHttpRequest::Execute(). Once that call is finished,
1005 // CHttpRequest may no longer be accessible.
1006 //
1008  void* user_data,
1009  unsigned int failure_count)
1010 {
1011  if ( !user_data ) return 0; // error, stop
1012 
1013  SAdjustData* adj = reinterpret_cast<SAdjustData*>(user_data);
1014  if (failure_count == (unsigned int)(-1) && !adj->m_IsService)
1015  return -1; // no new URL
1016 
1017  CHttpRequest* req = adj->m_Request;
1018  CRef<CHttpResponse> resp = req->m_Response;
1019 
1020  if (failure_count && failure_count != (unsigned int)(-1)) {
1021  // On the following errors do not retry, abort the request.
1022  switch ( resp->GetStatusCode() ) {
1023  case 400:
1024  case 403:
1025  case 404:
1026  case 405:
1027  case 406:
1028  case 410:
1029  return 0; // stop
1030  default:
1031  break;
1032  }
1033  if (!adj->m_IsService)
1034  return 1; // ok to retry
1035  }
1036  _ASSERT(!failure_count/*redirect*/ || adj->m_IsService);
1037 
1038  // Update location if it's different from the original URL.
1039  auto loc = make_c_unique(ConnNetInfo_URL(net_info));
1040  if (loc) {
1041  CUrl url(loc.get());
1042  if (failure_count) {
1043  _ASSERT(adj->m_IsService);
1044  bool adjust;
1045  if (req->m_AdjustUrl)
1046  adjust = req->m_AdjustUrl->AdjustUrl(url);
1047  else {
1048  url.Adjust(req->m_Url,
1052  adjust = true;
1053  }
1054  if ( adjust ) {
1055  // Re-read the url and save that in the response.
1056  string new_url = url.ComposeUrl(CUrlArgs::eAmp_Char);
1057  if (!ConnNetInfo_ParseURL(net_info, new_url.c_str())) {
1058  NCBI_THROW(CHttpSessionException, eConnFailed,
1059  "Cannot parse URL " + new_url);
1060  }
1061  if (!(loc = make_c_unique(ConnNetInfo_URL(net_info)))) {
1062  NCBI_THROW(CHttpSessionException, eConnFailed,
1063  "Cannot obtain updated URL");
1064  }
1065  }
1066  }
1067  resp->m_Location.SetUrl(loc.get());
1068  } else {
1069  NCBI_THROW(CHttpSessionException, eConnFailed,
1070  "Cannot obtain original URL");
1071  }
1072 
1073  // Discard old cookies, add those for the new location.
1074  req->x_AddCookieHeader(resp->m_Location, false);
1075  string headers = req->m_Headers->GetHttpHeader();
1076  if (!ConnNetInfo_OverrideUserHeader(net_info, headers.c_str())) {
1077  NCBI_THROW(CHttpSessionException, eConnFailed,
1078  "Cannot set HTTP header(s)");
1079  }
1080  return 1; // proceed
1081 }
1082 
1083 
1085 {
1086  m_Timeout = timeout;
1087  return *this;
1088 }
1089 
1090 
1092  unsigned int usec)
1093 {
1094  m_Timeout.Set(sec, usec);
1095  return *this;
1096 }
1097 
1098 
1100 {
1101  m_Deadline = deadline;
1102  return *this;
1103 }
1104 
1105 
1107 {
1108  m_RetryProcessing = on_off;
1109  return *this;
1110 }
1111 
1112 
1114 {
1115  m_Timeout = param.GetTimeout();
1116  m_Retries = param.GetRetries();
1117  m_Proxy = param.GetProxy();
1118  m_Deadline = param.GetDeadline();
1120  Headers().Merge(param.GetHeaders());
1124  }
1125 }
1126 
1127 
1128 ///////////////////////////////////////////////////////
1129 // CHttpSession_Base::
1130 //
1131 
1132 
1134  : m_Protocol(protocol),
1135  m_HttpFlags(0)
1136 {
1137 }
1138 
1139 
1141 {
1142  return CHttpRequest(*this, url, EReqMethod(method), param);
1143 }
1144 
1145 
1147  const CTimeout& timeout,
1148  THttpRetries retries)
1149 {
1150  CHttpRequest req = NewRequest(url, eGet);
1151  req.SetTimeout(timeout);
1152  req.SetRetries(retries);
1153  return req.Execute();
1154 }
1155 
1156 
1158  CTempString data,
1159  CTempString content_type,
1160  const CTimeout& timeout,
1161  THttpRetries retries)
1162 {
1163  CHttpRequest req = NewRequest(url, ePost);
1164  req.SetTimeout(timeout);
1165  req.SetRetries(retries);
1166  if ( content_type.empty() ) {
1167  content_type = kContentType_FormUrlEnc;
1168  }
1169  req.Headers().SetValue(CHttpHeaders::eContentType, content_type);
1170  if ( !data.empty() ) {
1171  req.ContentStream() << data;
1172  }
1173  return req.Execute();
1174 }
1175 
1176 
1178  CTempString data,
1179  CTempString content_type,
1180  const CTimeout& timeout,
1181  THttpRetries retries)
1182 {
1183  CHttpRequest req = NewRequest(url, ePut);
1184  req.SetTimeout(timeout);
1185  req.SetRetries(retries);
1186  if ( content_type.empty() ) {
1187  content_type = kContentType_FormUrlEnc;
1188  }
1189  req.Headers().SetValue(CHttpHeaders::eContentType, content_type);
1190  if ( !data.empty() ) {
1191  req.ContentStream() << data;
1192  }
1193  return req.Execute();
1194 }
1195 
1196 
1197 // Mutex protecting session cookies.
1199 
1200 
1202  const CUrl* url)
1203 {
1204  CFastMutexGuard lock(s_SessionMutex);
1205  ITERATE(CHttpHeaders::THeaderValues, it, cookies) {
1207  }
1208 }
1209 
1210 
1211 string CHttpSession_Base::x_GetCookies(const CUrl& url) const
1212 {
1213  string cookies;
1214  CFastMutexGuard lock(s_SessionMutex);
1215  CHttpCookie_CI it = m_Cookies.begin(url);
1216  for (; it; ++it) {
1217  if ( !cookies.empty() ) {
1218  cookies += "; ";
1219  }
1220  cookies += it->AsString(CHttpCookie::eHTTPRequest);
1221  }
1222  return cookies;
1223 }
1224 
1225 
1226 void CHttpSession_Base::SetCredentials(shared_ptr<CTlsCertCredentials> cred)
1227 {
1228  if (m_Credentials) {
1230  "Session credentials already set");
1231  }
1232  m_Credentials = cred;
1233 }
1234 
1235 
1237  : m_Headers(new CHttpHeaders),
1238  m_Timeout(CTimeout::eDefault),
1239  m_Deadline(CTimeout::eDefault),
1240  m_RetryProcessing(ESwitch::eDefault)
1241 {
1242 }
1243 
1244 
1246 {
1247  m_Headers->Assign(headers);
1248  return *this;
1249 }
1250 
1251 
1253 {
1254  m_Headers->SetValue(header, value);
1255  return *this;
1256 }
1257 
1258 
1260 {
1261  m_Headers->AddValue(header, value);
1262  return *this;
1263 }
1264 
1265 
1267 {
1268  m_Timeout = timeout;
1269  return *this;
1270 }
1271 
1272 
1274 {
1275  m_Retries = retries;
1276  return *this;
1277 }
1278 
1279 
1280 CHttpParam& CHttpParam::SetCredentials(shared_ptr<CTlsCertCredentials> credentials)
1281 {
1282  m_Credentials = credentials;
1283  return *this;
1284 }
1285 
1286 
1287 CHttpResponse g_HttpGet(const CUrl& url, const CHttpParam& param)
1288 {
1289  CRef<CHttpSession> session(new CHttpSession);
1290  session->SetCredentials(param.GetCredentials());
1291  CHttpRequest req = session->NewRequest(url, CHttpSession::eGet, param);
1292  return req.Execute();
1293 }
1294 
1295 
1297  const CTimeout& timeout,
1298  THttpRetries retries)
1299 {
1300  CHttpHeaders hdr;
1301  return g_HttpGet(url, hdr, timeout, retries);
1302 }
1303 
1304 
1306  const CHttpHeaders& headers,
1307  const CTimeout& timeout,
1308  THttpRetries retries)
1309 {
1310  CRef<CHttpSession> session(new CHttpSession);
1311  CHttpRequest req = session->NewRequest(url, CHttpSession::eGet);
1312  req.SetTimeout(timeout);
1313  req.SetRetries(retries);
1314  req.Headers().Merge(headers);
1315  return req.Execute();
1316 }
1317 
1318 
1320  CTempString data,
1321  const CHttpParam& param)
1322 {
1323  CRef<CHttpSession> session(new CHttpSession);
1324  session->SetCredentials(param.GetCredentials());
1325  CHttpRequest req = session->NewRequest(url, CHttpSession::ePost, param);
1326 
1330  }
1331 
1332  if (!data.empty()) {
1333  req.ContentStream() << data;
1334  }
1335 
1336  return req.Execute();
1337 }
1338 
1339 
1341  CTempString data,
1342  CTempString content_type,
1343  const CTimeout& timeout,
1344  THttpRetries retries)
1345 {
1346  CHttpHeaders hdr;
1347  return g_HttpPost(url, hdr, data, content_type, timeout, retries);
1348 }
1349 
1350 
1352  const CHttpHeaders& headers,
1353  CTempString data,
1354  CTempString content_type,
1355  const CTimeout& timeout,
1356  THttpRetries retries)
1357 {
1358  CRef<CHttpSession> session(new CHttpSession);
1359  CHttpRequest req = session->NewRequest(url, CHttpSession::ePost);
1360  req.SetTimeout(timeout);
1361  req.SetRetries(retries);
1362  req.Headers().Merge(headers);
1363 
1364  if ( content_type.empty() ) {
1365  if ( headers.HasValue(CHttpHeaders::eContentType) ) {
1368  }
1369  else {
1372  }
1373  }
1374  else {
1375  req.Headers().SetValue(CHttpHeaders::eContentType, content_type);
1376  }
1377 
1378  if ( !data.empty() ) {
1379  req.ContentStream() << data;
1380  }
1381 
1382  return req.Execute();
1383 }
1384 
1385 
1387  CTempString data,
1388  const CHttpParam& param)
1389 {
1390  CRef<CHttpSession> session(new CHttpSession);
1391  session->SetCredentials(param.GetCredentials());
1392  CHttpRequest req = session->NewRequest(url, CHttpSession::ePut, param);
1393 
1397  }
1398 
1399  if (!data.empty()) {
1400  req.ContentStream() << data;
1401  }
1402 
1403  return req.Execute();
1404 }
1405 
1406 
1408  CTempString data,
1409  CTempString content_type,
1410  const CTimeout& timeout,
1411  THttpRetries retries)
1412 {
1413  CHttpHeaders hdr;
1414  return g_HttpPut(url, hdr, data, content_type, timeout, retries);
1415 }
1416 
1417 
1419  const CHttpHeaders& headers,
1420  CTempString data,
1421  CTempString content_type,
1422  const CTimeout& timeout,
1423  THttpRetries retries)
1424 {
1425  CRef<CHttpSession> session(new CHttpSession);
1426  CHttpRequest req = session->NewRequest(url, CHttpSession::ePut);
1427  req.SetTimeout(timeout);
1428  req.SetRetries(retries);
1429  req.Headers().Merge(headers);
1430 
1431  if ( content_type.empty() ) {
1432  if ( headers.HasValue(CHttpHeaders::eContentType) ) {
1435  }
1436  else {
1439  }
1440  }
1441  else {
1442  req.Headers().SetValue(CHttpHeaders::eContentType, content_type);
1443  }
1444 
1445  if ( !data.empty() ) {
1446  req.ContentStream() << data;
1447  }
1448 
1449  return req.Execute();
1450 }
1451 
1452 
1453 ///////////////////////////////////////////////////////
1454 // CHttpSessionException::
1455 //
1456 
1457 
1459 {
1460  switch (GetErrCode()) {
1461  case eConnFailed: return "Connection failed";
1462  case eBadRequest: return "Bad request";
1463  case eBadContentType: return "Bad Content-Type";
1464  case eBadFormDataName: return "Bad form data name";
1465  case eBadFormData: return "Bad form data";
1466  case eBadStream: return "Bad stream";
1467  case eOther: return "Other error";
1468  default: return CException::GetErrCodeString();
1469  }
1470 }
1471 
1472 
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:4506
CTempString implements a light-weight string on top of a storage buffer whose lifetime management is ...
Definition: tempstr.hpp:65
CTimeException –.
Definition: ncbitime.hpp:2075
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
The NCBI C++ standard methods for dealing with std::string.
const char * file_name[]
std::ofstream out("events_result.xml")
main entry point for tests
static DLIST_TYPE *DLIST_NAME() last(DLIST_LIST_TYPE *list)
Definition: dlist.tmpl.h:51
char data[12]
Definition: iconv.c:80
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:3461
#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:3201
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:5353
bool HasZeroAtEnd(void) const
Definition: tempstr.hpp:1036
TErrCode GetErrCode(void) const
Get error code.
Definition: ncbistr.hpp:4455
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:6062
@ 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:4510
@ 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:3859
bool IsExpired(void) const
Check if the deadline is expired.
Definition: ncbitime.hpp:1855
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:3490
bool IsDefault() const
Definition: ncbitime.hpp:2723
void Set(EType type)
Set special value.
Definition: ncbitime.cpp:3577
@ eConvert
Error converting value from one format to another.
Definition: ncbitime.hpp:2080
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 GenericPointer< typename T::ValueType > T2 value
Definition: pointer.h:1227
const char * ConnNetInfo_GetValueInternal(const char *service, const char *param, char *value, size_t value_size, const char *def_value)
#define CONN_HOST_LEN
#define CONN_USER_LEN
Definition: ncbi_connutil.h:99
#define CONN_PASS_LEN
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, bool is_service)
CHttpRequest * m_Request
const bool m_IsService
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 Wed May 15 15:03:22 2024 by modify_doxy.py rev. 669887