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

Go to the SVN repository for this file.

1 /* $Id: cgictx.cpp 80619 2017-12-27 15:07:37Z grichenk $
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: Eugene Vasilchenko
27 *
28 * File Description:
29 * Definition CGI application class and its context class.
30 *
31 */
32 
33 #include <ncbi_pch.hpp>
34 #include <corelib/ncbistd.hpp>
35 #include <corelib/ncbireg.hpp>
36 #include <corelib/ncbidiag.hpp>
37 #include <corelib/request_ctx.hpp>
39 #include <cgi/ncbires.hpp>
40 #include <cgi/cgictx.hpp>
41 #include <cgi/cgi_util.hpp>
42 #include <cgi/cgi_session.hpp>
43 #include <cgi/cgiapp.hpp>
44 #include <cgi/error_codes.hpp>
45 
46 
47 #ifdef NCBI_OS_UNIX
48 # ifdef _AIX32 // version 3.2 *or higher*
49 # include <strings.h> // needed for bzero()
50 # endif
51 # include <sys/time.h>
52 # include <unistd.h> // needed for select() on some platforms
53 #endif
54 
55 
56 #define NCBI_USE_ERRCODE_X Cgi_Application
57 
58 
60 
61 
62 /////////////////////////////////////////////////////////////////////////////
63 // CCgiServerContext::
64 //
65 
67 {
68  return;
69 }
70 
71 
72 
73 /////////////////////////////////////////////////////////////////////////////
74 // CCtxMsg::
75 //
76 
78 {
79  return;
80 }
81 
82 
83 
84 /////////////////////////////////////////////////////////////////////////////
85 // CCtxMsgString::
86 //
87 
88 const char* CCtxMsgString::sm_nl = "\n";
89 
90 
92 {
93  return;
94 }
95 
96 
98 {
99  return os << m_Message << sm_nl;
100 }
101 
102 
103 /////////////////////////////////////////////////////////////////////////////
104 // CCgiContext::
105 //
106 
108  const CNcbiArguments* args,
109  const CNcbiEnvironment* env,
110  CNcbiIstream* inp,
111  CNcbiOstream* out,
112  int ifd,
113  int ofd,
114  size_t errbuf_size,
116  : m_App(&app),
117  m_Request(new CCgiRequest(args ? args : &app.GetArguments(),
118  env ? env : &app.GetEnvironment(),
119  inp, flags, ifd, errbuf_size)),
120  m_Response(out, ofd),
121  m_SecureMode(eSecure_NotSet),
122  m_StatusCode(CCgiException::eStatusNotSet)
123 {
124  m_Response.SetRequestMethod(m_Request->GetRequestMethod());
126 
129  }
130  x_InitSession(flags);
131  return;
132 }
133 
134 
136  CNcbiIstream* is,
137  CNcbiOstream* os,
139  : m_App(&app),
140  m_Request(new CCgiRequest()),
141  m_Response(os, -1),
142  m_SecureMode(eSecure_NotSet),
143  m_StatusCode(CCgiException::eStatusNotSet)
144 {
145  m_Request->Deserialize(*is,flags);
146  x_InitSession(flags);
147  return;
148 }
149 
150 
152  const CNcbiArguments* args,
153  const CNcbiEnvironment* env,
154  CNcbiIstream* inp,
155  CNcbiOstream* out,
156  int ifd,
157  int ofd,
158  size_t errbuf_size,
160  : m_App(nullptr),
161  m_Request(new CCgiRequest(args, env, inp, flags, ifd, errbuf_size)),
162  m_Response(out, ofd),
163  m_SecureMode(eSecure_NotSet),
164  m_StatusCode(CCgiException::eStatusNotSet)
165 {
166  m_Response.SetRequestMethod(m_Request->GetRequestMethod());
168 
171  }
172  x_InitSession(flags, session_storage);
173  return;
174 }
175 
176 
178 {
179  if ( !m_App ) {
180  NCBI_THROW(CCgiAppException, eApp, "NULL CCgiApplication in CCgiContext");
181  }
182  return *m_App;
183 }
184 
185 
187  ICgiSessionStorage* session_storage)
188 {
190  ICgiSessionStorage* impl = session_storage;
191  if (!session_storage && m_App) {
192  impl = m_App->GetSessionStorage(params);
193  }
194  m_Session.reset(new CCgiSession(*m_Request,
195  impl,
196  params.m_ImplOwner,
197  params.m_CookieEnabled ?
200  );
201  m_Session->SetSessionIdName(params.m_SessionIdName);
202  m_Session->SetSessionCookieDomain(params.m_SessionCookieDomain);
203  m_Session->SetSessionCookiePath(params.m_SessionCookiePath);
204  m_Session->SetSessionCookieExpTime(params.m_SessionCookieExpTime);
205 
206  m_Request->x_SetSession(*m_Session);
208  string track_cookie_value = RetrieveTrackingId();
209  bool bad_tracking_cookie = false;
210  if ((flags & CCgiRequest::fSkipDiagProperties) == 0) {
211  try {
213  ctx.SetSessionID(track_cookie_value);
214  if (ctx.GetSessionID() != track_cookie_value) {
215  // Bad session-id was ignored
216  track_cookie_value = ctx.SetSessionID();
217  }
218  }
219  catch (CRequestContextException& e) {
221  bad_tracking_cookie = true;
222  }
223  }
224  if( !bad_tracking_cookie && !TCGI_DisableTrackingCookie::GetDefault() ) {
225  m_Response.SetTrackingCookie(TCGI_TrackingCookieName::GetDefault(),
226  track_cookie_value,
227  TCGI_TrackingCookieDomain::GetDefault(),
228  TCGI_TrackingCookiePath::GetDefault());
229  }
230 
231  GetSelfURL();
233 }
234 
235 
237 {
238  return;
239 }
240 
241 
243 {
244  return x_GetApp().GetConfig();
245 }
246 
247 
249 {
250  return x_GetApp().GetConfig();
251 }
252 
253 
255 {
256  return x_GetApp().GetResource();
257 }
258 
259 
261 {
262  return x_GetApp().GetResource();
263 }
264 
265 
267 {
268  CCgiServerContext* context = m_ServerContext.get();
269  if ( !context ) {
270  context = x_GetApp().LoadServerContext(const_cast<CCgiContext&>(*this));
271  if ( !context ) {
272  ERR_POST_X(12, "CCgiContext::GetServerContext: no server context set");
273  throw runtime_error("no server context set");
274  }
275  const_cast<CCgiContext&>(*this).m_ServerContext.reset(context);
276  }
277  return *context;
278 }
279 
280 
281 const CCgiEntry& CCgiContext::GetRequestValue(const string& name,
282  bool* is_found)
283  const
284 {
285  pair<TCgiEntriesCI, TCgiEntriesCI> range =
287 
288  if (range.second == range.first) {
289  if ( is_found ) {
290  *is_found = false;
291  }
292  static CSafeStatic<CCgiEntry> s_EmptyCgiEntry;
293  return s_EmptyCgiEntry.Get();
294  }
295  if ( is_found ) {
296  *is_found = true;
297  }
298 
299  const CCgiEntry& value = range.first->second;
300  while (++range.first != range.second) {
301  if (range.first->second != value) {
302  THROW1_TRACE(runtime_error,
303  "duplicate entries in request with name: " +
304  name + ": " + value.GetValue() + "!=" +
305  range.first->second.GetValue());
306  }
307  }
308  return value;
309 }
310 
311 
312 void CCgiContext::RemoveRequestValues(const string& name)
313 {
314  GetRequest().GetEntries().erase(name);
315 }
316 
317 
318 void CCgiContext::AddRequestValue(const string& name, const CCgiEntry& value)
319 {
321 }
322 
323 
324 void CCgiContext::ReplaceRequestValue(const string& name,
325  const CCgiEntry& value)
326 {
327  RemoveRequestValues(name);
328  AddRequestValue(name, value);
329 }
330 
331 
332 const string& CCgiContext::GetSelfURL(void) const
333 {
334  if ( !m_SelfURL.empty() )
335  return m_SelfURL;
336 
337  // First check forwarded URLs
338  const string& caf_url = GetRequest().GetRandomProperty("CAF_URL");
339  if ( !caf_url.empty() ) {
340  m_SelfURL = caf_url;
341  return m_SelfURL;
342  }
343 
344  bool secure
345  = AStrEquiv
346  (GetRequest().GetRandomProperty("HTTPS", false), "on",
347  PNocase())
348  || AStrEquiv
349  (GetRequest().GetRandomProperty("X_FORWARDED_PROTO"), "https",
350  PNocase());
351 
352  string host_port;
353  // Check HTTP_X_FORWARDED_HOST for host:port
354  const string& x_fwd_host = GetRequest().GetRandomProperty("X_FORWARDED_HOST");
355  if (!x_fwd_host.empty()) {
356  host_port = x_fwd_host;
357  }
358  else {
359  string server(GetRequest().GetProperty(eCgi_ServerName));
360  if (server.empty()) {
361  return kEmptyStr;
362  }
363 
364  host_port = server;
365  string port = GetRequest().GetProperty(eCgi_ServerPort);
366  // Skip port if it's default for the selected scheme
367  if ((secure && port == "443") || (!secure && port == "80")
368  || (server.size() >= port.size() + 2 && NStr::EndsWith(server, port)
369  && server[server.size() - port.size() - 1] == ':')) {
370  port.erase();
371  }
372  if (!port.empty()) {
373  host_port += ':';
374  host_port += port;
375  }
376  }
377 
378  // Compose self URL
379  m_SecureMode = secure ? eSecure_On : eSecure_Off;
380  m_SelfURL = secure ? "https://" : "http://";
381  m_SelfURL += host_port;
382 
383  string script_uri;
384  script_uri = GetRequest().GetRandomProperty("SCRIPT_URL", false);
385  if ( script_uri.empty() ) {
386  script_uri = GetRequest().GetProperty(eCgi_ScriptName);
387  }
388  // Remove args if any
389  size_t arg_pos = script_uri.find('?');
390  if (arg_pos != NPOS) {
391  script_uri.resize(arg_pos);
392  }
393  // (replace adjacent '//' to work around a bug in the "www.ncbi" proxy;
394  // it should not hurt, and may help with similar proxies outside NCBI)
395  m_SelfURL += NStr::ReplaceInPlace(script_uri, "//", "/");
396 
397  return m_SelfURL;
398 }
399 
400 
401 bool CCgiContext::IsSecure(void) const
402 {
403  if (m_SecureMode == eSecure_NotSet) {
406  (CTempStringEx(GetSelfURL(), 0, 8), "https://")
408  (GetRequest().GetRandomProperty("HTTPS", false), "on")
410  (GetRequest().GetRandomProperty("X_FORWARDED_PROTO"), "https")
412  }
413  return m_SecureMode == eSecure_On;
414 }
415 
416 
419 {
420 #if defined(NCBI_OS_UNIX)
421  int ifd = m_Request->GetInputFD();
422  int ofd = m_Response.GetOutputFD();
423  int nfds = max(ifd, ofd) + 1;
424  if (nfds == 0) {
425  return 0;
426  }
427 
428  fd_set readfds, writefds;
429  FD_ZERO(&readfds);
430  if (ifd >= 0) {
431  FD_SET(ifd, &readfds);
432  }
433  FD_ZERO(&writefds);
434  if (ofd >= 0) {
435  FD_SET(ofd, &writefds);
436  }
437  struct timeval tv;
438  tv.tv_sec = timeout->sec;
439  tv.tv_usec = timeout->usec;
440  ::select(nfds, &readfds, &writefds, NULL, &tv);
441 
442  TStreamStatus result = 0;
443  if (ifd >= 0 && FD_ISSET(ifd, &readfds)) {
444  result |= fInputReady;
445  }
446  if (ofd >= 0 && FD_ISSET(ofd, &writefds)) {
447  result |= fOutputReady;
448  }
449  return result;
450 #else
451  return 0;
452 #endif
453 }
454 
455 static inline bool s_CheckValueForTID(const string& value, string& tid)
456 {
457  string part1, part2;
458  NStr::SplitInTwo(value, "@", part1, part2);
459  if (NStr::EndsWith(part2, "SID")) {
460  tid = part2;
461  return true;
462  }
463  return false;
464 }
465 
466 static inline bool s_CheckCookieForTID(const CCgiCookies& cookies,
467  const string& cookie_name, string& tid)
468 {
469  const CCgiCookie* cookie = cookies.Find(cookie_name);
470 
471  return cookie != NULL && s_CheckValueForTID(cookie->GetValue(), tid);
472 }
473 
474 static inline bool s_CheckRequestEntryForTID(const CCgiRequest* request,
475  const string& entry_name, string& tid)
476 {
477  bool is_found = false;
478  const CCgiEntry* entry = &request->GetEntry(entry_name, &is_found);
479 
480  return is_found && s_CheckValueForTID(entry->GetValue(), tid);
481 }
482 
483 
484 // Check if TID is non-empty and (if Discard_UNK_SESSION is set) not UNK_SESSION.
485 NCBI_PARAM_DECL(bool, CGI, Discard_UNK_SESSION);
486 NCBI_PARAM_DEF_EX(bool, CGI, Discard_UNK_SESSION, false, eParam_NoThread,
487  NCBI_CGI_DISCARD_UNK_SESSION);
488 typedef NCBI_PARAM_TYPE(CGI, Discard_UNK_SESSION) TParamDiscardUnkSession;
489 
490 static inline bool s_IsTID(const string& tid)
491 {
492  if (tid.empty()) return false;
493  if (TParamDiscardUnkSession::GetDefault())
494  return tid != "UNK_SESSION";
495  return true;
496 }
497 
498 
500 {
501  if ( !m_TrackingId.empty() ) {
502  // Use cached value
503  return m_TrackingId;
504  }
505 
506  static const char* cookie_or_entry_name_1 = "WebCubbyUser";
507  static const char* cookie_or_entry_name_2 = "WebEnv";
508 
509  // The order of checking SID is:
510  // - Check entries (GET and POST) for ncbi_sid.
511  // - Check cookies for WebCubbyUser, ncbi_sid and WebEnv.
512  // - Check entries for WebCubbyUser and WebEnv.
513  // - Generate a new SID.
514 
515  bool is_found = false;
516  const CCgiEntry* entry =
517  &m_Request->GetEntry(TCGI_TrackingCookieName::GetDefault(), &is_found);
518  if (is_found && s_IsTID(entry->GetValue())) {
519  return entry->GetValue();
520  }
521 
522  const CCgiCookies& cookies = m_Request->GetCookies();
523  string tid;
524 
525  if (s_CheckCookieForTID(cookies, cookie_or_entry_name_1, tid))
526  return tid;
527  const CCgiCookie* cookie = cookies.Find(
528  TCGI_TrackingCookieName::GetDefault(), kEmptyStr, kEmptyStr);
529  if (cookie && s_IsTID(cookie->GetValue())) {
530  return cookie->GetValue();
531  }
532  if (s_CheckCookieForTID(cookies, cookie_or_entry_name_2, tid))
533  return tid;
534  if (s_CheckRequestEntryForTID(m_Request.get(), cookie_or_entry_name_1, tid))
535  return tid;
536  if (s_CheckRequestEntryForTID(m_Request.get(), cookie_or_entry_name_2, tid))
537  return tid;
538 
539  string tag_name = TCGI_TrackingTagName::GetDefault();
540  NStr::ReplaceInPlace(tag_name, "-", "_");
542  m_Request->GetRandomProperty(tag_name, true));
543  if (s_IsTID(tid)) {
544  return tid;
545  }
546 
548  // If UNK_SESSION is set through HTTP_NCBI_SID, replace it with a valid SID
549  s_IsTID(CDiagContext::GetRequestContext().GetSessionID()) ?
552 }
553 
554 
556 {
557  m_StatusCode = code;
559 }
560 
561 
562 void CCgiContext::CheckStatus(void) const
563 {
565 
568  ex.SetStatus(m_StatusCode);
570 }
571 
572 
573 // Param controlling cross-origin resource sharing headers. If set to true,
574 // non-empty parameters for individual headers are used as values for the
575 // headers.
576 NCBI_PARAM_DECL(bool, CGI, CORS_Enable);
577 NCBI_PARAM_DEF_EX(bool, CGI, CORS_Enable, false,
578  eParam_NoThread, CGI_CORS_ENABLE);
579 typedef NCBI_PARAM_TYPE(CGI, CORS_Enable) TCORS_Enable;
580 
581 // Access-Control-Allow-Headers
582 NCBI_PARAM_DECL(string, CGI, CORS_Allow_Headers);
583 NCBI_PARAM_DEF_EX(string, CGI, CORS_Allow_Headers, "",
584  eParam_NoThread, CGI_CORS_ALLOW_HEADERS);
585 typedef NCBI_PARAM_TYPE(CGI, CORS_Allow_Headers) TCORS_AllowHeaders;
586 
587 // Access-Control-Allow-Methods
588 NCBI_PARAM_DECL(string, CGI, CORS_Allow_Methods);
589 NCBI_PARAM_DEF_EX(string, CGI, CORS_Allow_Methods, "GET, POST, OPTIONS",
590  eParam_NoThread, CGI_CORS_ALLOW_METHODS);
591 typedef NCBI_PARAM_TYPE(CGI, CORS_Allow_Methods) TCORS_AllowMethods;
592 
593 // Access-Control-Allow-Origin. Should be a list of domain suffixes
594 // separated by space (e.g. 'foo.com bar.org'). The Origin header
595 // sent by the client is matched against the list. If there's no match,
596 // CORS is not enabled. If matched, the client provided Origin is copied
597 // to the outgoing Access-Control-Allow-Origin. To allow any origin
598 // set the value to '*' (this should be a single char, not part of the list).
599 NCBI_PARAM_DECL(string, CGI, CORS_Allow_Origin);
600 NCBI_PARAM_DEF_EX(string, CGI, CORS_Allow_Origin, "ncbi.nlm.nih.gov",
601  eParam_NoThread, CGI_CORS_ALLOW_ORIGIN);
602 typedef NCBI_PARAM_TYPE(CGI, CORS_Allow_Origin) TCORS_AllowOrigin;
603 
604 // Access-Control-Allow-Credentials
605 NCBI_PARAM_DECL(bool, CGI, CORS_Allow_Credentials);
606 NCBI_PARAM_DEF_EX(bool, CGI, CORS_Allow_Credentials, false,
607  eParam_NoThread, CGI_CORS_ALLOW_CREDENTIALS);
608 typedef NCBI_PARAM_TYPE(CGI, CORS_Allow_Credentials) TCORS_AllowCredentials;
609 
610 // Access-Control-Expose-Headers
611 NCBI_PARAM_DECL(string, CGI, CORS_Expose_Headers);
612 NCBI_PARAM_DEF_EX(string, CGI, CORS_Expose_Headers, "",
613  eParam_NoThread, CGI_CORS_EXPOSE_HEADERS);
614 typedef NCBI_PARAM_TYPE(CGI, CORS_Expose_Headers) TCORS_ExposeHeaders;
615 
616 // Access-Control-Max-Age
617 NCBI_PARAM_DECL(string, CGI, CORS_Max_Age);
618 NCBI_PARAM_DEF_EX(string, CGI, CORS_Max_Age, "",
619  eParam_NoThread, CGI_CORS_MAX_AGE);
620 typedef NCBI_PARAM_TYPE(CGI, CORS_Max_Age) TCORS_MaxAge;
621 
622 
623 // Param to enable JQuery JSONP hack to allow cross-origin resource sharing
624 // for browsers that don't support CORS (e.g. IE versions earlier than 11).
625 // If it is set to true and the HTTP request contains entry "callback" whose
626 // value starts with "JQuery_" (case-insensitive, configurable), then:
627 // - Set the response Content-Type to "text/javascript"
628 // - Wrap the response content into: "JQuery_foobar(original_content)"
629 NCBI_PARAM_DECL(bool, CGI, CORS_JQuery_Callback_Enable);
630 NCBI_PARAM_DEF_EX(bool, CGI, CORS_JQuery_Callback_Enable, false,
631  eParam_NoThread, CGI_CORS_JQUERY_CALLBACK_ENABLE);
632 typedef NCBI_PARAM_TYPE(CGI, CORS_JQuery_Callback_Enable)
633  TCORS_JQuery_Callback_Enable;
634 
635 // If ever need to use a prefix other than "JQuery_" for the JQuery JSONP hack
636 // callback name (above). Use symbol '*' if any name is good.
637 NCBI_PARAM_DECL(string, CGI, CORS_JQuery_Callback_Prefix);
638 NCBI_PARAM_DEF_EX(string, CGI, CORS_JQuery_Callback_Prefix, "*",
639  eParam_NoThread, CGI_CORS_JQUERY_CALLBACK_PREFIX);
640 typedef NCBI_PARAM_TYPE(CGI, CORS_JQuery_Callback_Prefix)
641  TCORS_JQuery_Callback_Prefix;
642 
643 
644 static const char* kAC_Origin = "Origin";
645 static const char* kAC_RequestMethod = "Access-Control-Request-Method";
646 static const char* kAC_RequestHeaders = "Access-Control-Request-Headers";
647 static const char* kAC_AllowHeaders = "Access-Control-Allow-Headers";
648 static const char* kAC_AllowMethods = "Access-Control-Allow-Methods";
649 static const char* kAC_AllowOrigin = "Access-Control-Allow-Origin";
650 static const char* kAC_AllowCredentials = "Access-Control-Allow-Credentials";
651 static const char* kAC_ExposeHeaders = "Access-Control-Expose-Headers";
652 static const char* kAC_MaxAge = "Access-Control-Max-Age";
653 static const char* kSimpleHeaders =
654  " Accept Accept-Language Content-Language Content-Type";
655 static const char* kDefaultHeaders =
656  " Origin Cache-Control Expires Last-Modified Pragma X-Accept-Charset X-Accept"
657  " X-Requested-With NCBI-SID NCBI-PHID";
658 
659 
660 typedef list<string> TStringList;
661 
662 
663 // Check if the origin matches Allow-Origin parameter. Matching rules are:
664 // - If Allow-Origin is empty, return false.
665 // - If Allow-Origin contains an explicit list of origins, check if the
666 // input matches (case-sensitive) any of them, put this single value
667 // into the 'origin' argument and return true; return false otherwise.
668 // - If Allow-Origin is '*' (any), and the input origin is not empty,
669 // return true.
670 // - If Allow-Origin is '*' and the input origin is empty, check
671 // Allow-Credentials flag. Return false if it's enabled. Otherwise
672 // set 'origin' argument to '*' and return true.
673 static bool s_IsAllowedOrigin(const string& origin)
674 {
675  if ( origin.empty() ) {
676  // Origin header is not set - this is not a CORS request.
677  return false;
678  }
679  const string& allowed = TCORS_AllowOrigin::GetDefault();
680  if ( allowed.empty() ) {
681  return false;
682  }
683  if (NStr::Equal(allowed, "*")) {
684  // Accept any origin, it must be non-empty by now.
685  return true;
686  }
687 
688  TStringList origins;
689  NStr::Split(allowed, ", ", origins,
691  ITERATE(TStringList, it, origins) {
692  if (NStr::EndsWith(origin, *it, NStr::eCase)) {
693  return true;
694  }
695  }
696  return false;
697 }
698 
699 
700 // Check if the method is in the list of allowed methods. Empty
701 // method is a no-match. Comparison is case-sensitive.
702 static bool s_IsAllowedMethod(const string& method)
703 {
704  if ( method.empty() ) {
705  return false;
706  }
707  TStringList methods;
708  NStr::Split(TCORS_AllowMethods::GetDefault(), ", ", methods,
710  ITERATE(TStringList, it, methods) {
711  // Methods are case-sensitive.
712  if (*it == method) return true;
713  }
714  return false;
715 }
716 
717 
718 // Check if the (space separated) list of headers is a subset of the
719 // list of allowed headers. Empty list of headers is a match.
720 // Comparison is case-insensitive.
721 // NOTE: The following simple headers are matched automatically:
722 // Cache-Control
723 // Content-Language
724 // Expires
725 // Last-Modified
726 // Pragma
727 static bool s_IsAllowedHeaderList(const string& headers)
728 {
729  if ( headers.empty() ) {
730  // Empty header list is a subset of allowed headers.
731  return true;
732  }
733  TStringList allowed, requested;
734 
735  string ah = TCORS_AllowHeaders::GetDefault();
736  // Always allow simple headers.
737  ah += kSimpleHeaders;
738  ah += kDefaultHeaders;
739  NStr::ToUpper(ah);
740  NStr::Split(ah, ", ", allowed,
742  allowed.sort();
743 
744  string rh = headers;
745  NStr::ToUpper(rh);
746  NStr::Split(rh, ", ", requested,
748  requested.sort();
749 
750  TStringList::const_iterator ait = allowed.begin();
751  TStringList::const_iterator rit = requested.begin();
752  while (ait != allowed.end() && rit != requested.end()) {
753  if (*ait == *rit) {
754  ++rit; // found match
755  }
756  ++ait; // check next allowed
757  }
758  // Found match for each requested header?
759  return rit == requested.end();
760 }
761 
762 
763 inline string s_HeaderToHttp(const char* name)
764 {
765  string http_name(name);
766  return NStr::ToUpper(NStr::ReplaceInPlace(http_name, "-", "_"));
767 }
768 
769 
771  CCgiResponse& response)
772 {
773  // CORS requests handling, see http://www.w3.org/TR/cors/
774 
775  // Is CORS processing enabled?
776  if ( !TCORS_Enable::GetDefault() ) {
777  return false; // CORS disabled, so -- do regular request processing
778  }
779 
780  const CCgiRequest::ERequestMethod method = request.GetRequestMethod();
781 
782  // Is this a standard CORS request?
783  const string& origin = request.GetRandomProperty(s_HeaderToHttp(kAC_Origin));
784  if ( origin.empty() ) {
785  // Is this a JQuery based hack for browsers that don't yet support CORS?
786  string jquery_callback = request.GetEntry("callback");
787  if (TCORS_JQuery_Callback_Enable::GetDefault()
788  && (method == CCgiRequest::eMethod_GET ||
789  method == CCgiRequest::eMethod_POST ||
790  method == CCgiRequest::eMethod_Other)
791  && !jquery_callback.empty()) {
792  string prefix = TCORS_JQuery_Callback_Prefix::GetDefault();
793  if (prefix == "*" ||
794  NStr::StartsWith(jquery_callback, prefix, NStr::eNocase)) {
795  // Activate JQuery hack;
796  // skip standard CORS processing; do regular request processing
797  response.m_JQuery_Callback = jquery_callback;
798  }
799  }
800  return false;
801  }
802 
803  if ( !s_IsAllowedOrigin(origin) ) {
804  // CORS with invalid origin -- terminate request.
805  response.DisableTrackingCookie();
807  response.WriteHeader();
808  return true;
809  }
810 
811  _ASSERT(!origin.empty());
812 
813  // Is this a preflight CORS request?
814  if (method == CCgiRequest::eMethod_OPTIONS) {
815  const string& method_str = request.GetRandomProperty
817  const string& headers = request.GetRandomProperty
819  if (!s_IsAllowedMethod(method_str) || !s_IsAllowedHeaderList(headers)) {
820  // This is CORS request, but the method or headers are not allowed.
821  response.DisableTrackingCookie();
823  response.WriteHeader();
824  return true;
825  }
826  // Yes, it's a preflight CORS request, so -- set and send back
827  // the required headers, and don't do regular (non-CORS) request
828  // processing
829  response.SetHeaderValue(kAC_AllowOrigin, origin);
830  if ( TCORS_AllowCredentials::GetDefault() ) {
831  response.SetHeaderValue(kAC_AllowCredentials, "true");
832  }
833  const string& allow_methods = TCORS_AllowMethods::GetDefault();
834  if ( !allow_methods.empty() ) {
835  response.SetHeaderValue(kAC_AllowMethods, allow_methods);
836  }
837  string allow_headers = TCORS_AllowHeaders::GetDefault();
838  allow_headers += kDefaultHeaders;
839  if ( !allow_headers.empty() ) {
840  // Make comma-separated list.
841  TStringList allowed_list;
842  NStr::Split(allow_headers, ", ", allowed_list,
844  allow_headers = NStr::Join(allowed_list, ", ");
845  response.SetHeaderValue(kAC_AllowHeaders, allow_headers);
846  }
847  const string& max_age = TCORS_MaxAge::GetDefault();
848  if ( !max_age.empty() ) {
849  response.SetHeaderValue(kAC_MaxAge, max_age);
850  }
851  response.DisableTrackingCookie();
852  response.RemoveHeaderValue("NCBI-PHID");
853  response.WriteHeader();
854  // This was CORS preflight request (valid or not) - skip normap processing.
855  return true;
856  }
857 
858  // This is a normal CORS request (not a preflight), so -- set the required
859  // CORS headers, and then do regular (non-CORS) request processing
860  response.SetHeaderValue(kAC_AllowOrigin, origin);
861  if ( TCORS_AllowCredentials::GetDefault() ) {
862  response.SetHeaderValue(kAC_AllowCredentials, "true");
863  }
864  string exp_headers = TCORS_ExposeHeaders::GetDefault();
865  if ( !exp_headers.empty() ) {
866  response.SetHeaderValue(kAC_ExposeHeaders, exp_headers);
867  }
868 
869  // Proceed to ProcessRequest()
870  return false;
871 }
872 
873 
static const char * kSimpleHeaders
Definition: cgictx.cpp:653
string m_SelfURL
Definition: cgictx.hpp:279
bool AStrEquiv(const Arg1 &x, const Arg2 &y, Pred pr)
Check equivalence of arguments using predicate.
Definition: ncbistr.hpp:4763
int TFlags
Startup initialization.
Definition: ncbicgi.hpp:695
bool IsSecure(void) const
Check if the current scheme is secure (https) or not (http).
Definition: cgictx.cpp:401
Set client-ip and session-id properties for logging.
Definition: ncbicgi.hpp:717
const string & GetProperty(ECgiProp prop) const
Get value of a "standard" property (return empty string if not defined)
Definition: ncbicgi.cpp:1418
static const char * kAC_AllowHeaders
Definition: cgictx.cpp:647
static const char * kAC_Origin
Definition: cgictx.cpp:644
CGI
Definition: cgiapp.hpp:510
NCBI_PARAM_DEF_EX(bool, CGI, Discard_UNK_SESSION, false, eParam_NoThread, NCBI_CGI_DISCARD_UNK_SESSION)
static const char * kDefaultHeaders
Definition: cgictx.cpp:655
CSafeStatic<>::
static const unsigned char msg[]
Definition: ccm.c:378
static const char * kAC_RequestMethod
Definition: cgictx.cpp:645
Unknown method, use GetRequestMethodName to read.
Definition: ncbicgi.hpp:900
unsigned int usec
microseconds (modulo 1,000,000)
Definition: ncbi_types.h:78
CCgiApplication & x_GetApp(void) const
Definition: cgictx.cpp:177
string m_SessionCookiePath
Definition: cgictx.hpp:347
virtual ~CCgiContext(void)
Definition: cgictx.cpp:236
void SetTrackingCookie(const string &name, const string &value, const string &domain, const string &path, const CTime &exp_time=CTime())
Definition: ncbicgir.cpp:536
std::ofstream out("events_result.xml")
main entry point for tests
static string & ReplaceInPlace(string &src, const string &search, const string &replace, SIZE_TYPE start_pos=0, SIZE_TYPE max_replace=0, SIZE_TYPE *num_replace=0)
Replace occurrences of a substring within a string.
Definition: ncbistr.cpp:3306
int GetOutputFD(void) const
Get file descriptor of the output stream (-1 if not applicable)
Definition: ncbicgir.hpp:391
EOwnership m_ImplOwner
Definition: cgictx.hpp:343
Case insensitive compare.
Definition: ncbistr.hpp:1192
Internal value - code not set.
const CCgiCookies & Cookies(void) const
Definition: ncbicgir.hpp:381
const string & GetRandomProperty(const string &key, bool http=true) const
Get value of a random client property; if "http" is TRUE then add prefix "HTTP_" to the property name...
Definition: ncbicgi.cpp:1424
CDiagContext & GetDiagContext(void)
Get diag context instance.
Definition: logging.cpp:818
CNcbiEnvironment –.
Definition: ncbienv.hpp:103
unique_ptr< CCgiRequest > m_Request
Definition: cgictx.hpp:268
CCgiCookie * Find(const string &name, const string &domain, const string &path)
Return NULL if cannot find this exact cookie.
Definition: ncbicgi.cpp:684
static bool s_CheckValueForTID(const string &value, string &tid)
Definition: cgictx.cpp:455
CCgiRequest::
Definition: ncbicgi.hpp:676
const CCgiEntry & GetRequestValue(const string &name, bool *is_found=0) const
Definition: cgictx.cpp:281
const_iterator_pair equal_range(const key_type &key) const
Definition: map.hpp:296
void AddRequestValue(const string &name, const CCgiEntry &value)
Definition: cgictx.cpp:318
CNcbiOstream & WriteHeader(void) const
Write HTTP response header to the output stream.
Definition: ncbicgir.hpp:396
static bool s_IsAllowedOrigin(const string &origin)
Definition: cgictx.cpp:673
virtual ~CCtxMsgString(void)
Definition: cgictx.cpp:91
static const char * kAC_AllowCredentials
Definition: cgictx.cpp:650
list< string > TStringList
Definition: cgictx.cpp:660
void SetHeaderValue(const string &name, const string &value)
Definition: ncbicgir.cpp:151
#define NULL
Definition: ncbistd.hpp:225
CTime m_SessionCookieExpTime
Definition: cgictx.hpp:348
string GetSessionID(void) const
Session ID.
#define kEmptyStr
Definition: ncbistr.hpp:121
Merge adjacent delimiters.
Definition: ncbistr.hpp:2434
virtual ~CCtxMsg(void)
Definition: cgictx.cpp:77
static bool s_IsTID(const string &tid)
Definition: cgictx.cpp:490
string m_Message
Definition: cgictx.hpp:109
Static variables safety - create on demand, destroy on application termination.
void x_SetSession(const CCgiSession &session)
Definition: ncbicgir.hpp:416
void ReplaceRequestValue(const string &name, const CCgiEntry &value)
Definition: cgictx.cpp:324
void SetRequestMethod(CCgiRequest::ERequestMethod method)
Set HTTP request method.
Definition: ncbicgir.hpp:422
static const char * kAC_ExposeHeaders
Definition: cgictx.cpp:651
static const char * kAC_MaxAge
Definition: cgictx.cpp:652
#define ITERATE(Type, Var, Cont)
ITERATE macro to sequence through container elements.
Definition: ncbimisc.hpp:795
#define NPOS
Definition: ncbistr.hpp:131
bool IsSetSessionID(void) const
CCgiSession –.
Definition: cgi_session.hpp:61
static string Join(const TContainer &arr, const CTempString &delim)
Join strings using the specified delimiter.
Definition: ncbistr.hpp:2633
#define ERR_POST_X(err_subcode, message)
Error posting with default error code and given error subcode.
Definition: ncbidiag.hpp:544
static bool s_CheckCookieForTID(const CCgiCookies &cookies, const string &cookie_name, string &tid)
Definition: cgictx.cpp:466
CCgiServerContext & x_GetServerContext(void) const
Definition: cgictx.cpp:266
Defines CRequestContext class for NCBI C++ diagnostic API.
#define THROW1_TRACE(exception_class, exception_arg)
Throw trace.
Definition: ncbiexpt.hpp:258
void SetSecure(bool secure)
Set secure connection flag.
Definition: ncbicgi.hpp:322
const CNcbiResource & GetResource(void) const
Get server 'resource'. Throw exception if the resource is not set.
Definition: cgiapp.hpp:79
const CNcbiRegistry & GetConfig(void) const
Definition: cgictx.cpp:242
#define END_NCBI_SCOPE
End previously defined NCBI scope.
Definition: ncbistl.hpp:101
iterator insert(const value_type &val)
Definition: map.hpp:305
static const char * kAC_RequestHeaders
Definition: cgictx.cpp:646
ESecureMode m_SecureMode
Definition: cgictx.hpp:281
CCgiCookie::
Definition: ncbicgi.hpp:68
CCgiContext(CCgiApplication &app, const CNcbiArguments *args=0, const CNcbiEnvironment *env=0, CNcbiIstream *inp=0, CNcbiOstream *out=0, int ifd=-1, int ofd=-1, size_t errbuf_size=256, CCgiRequest::TFlags flags=0)
Definition: cgictx.cpp:107
const string & GetSelfURL(void) const
Using HTTP environment variables, compose the CGI's own URL as: SCHEME://SERVER_NAME[:SERVER_PORT]/SC...
Definition: cgictx.cpp:332
void RemoveRequestValues(const string &name)
Definition: cgictx.cpp:312
static bool SplitInTwo(const CTempString str, const CTempString delim, string &str1, string &str2, TSplitFlags flags=0)
Split a string into two pieces using the specified delimiters.
Definition: ncbistr.cpp:3455
static const char * sm_nl
Definition: cgictx.hpp:106
unique_ptr< CCgiSession > m_Session
Definition: cgictx.hpp:270
static bool ProcessCORSRequest(const CCgiRequest &request, CCgiResponse &response)
Process cross-origin resource sharing (CORS) request.
Definition: cgictx.cpp:770
#define nullptr
Definition: ncbimisc.hpp:45
unsigned int sec
seconds
Definition: ncbi_types.h:77
static bool StartsWith(const CTempString str, const CTempString start, ECase use_case=eCase)
Check if a string starts with a specified prefix value.
Definition: ncbistr.hpp:5141
static HENV env
Definition: transaction2.c:38
IO_PREFIX::ostream CNcbiOstream
Portable alias for ostream.
Definition: ncbistre.hpp:142
void x_SetStatus(CCgiException::EStatusCode code, const string &msg) const
Definition: cgictx.cpp:555
const CNcbiResource & GetResource(void) const
Definition: cgictx.cpp:254
typedef NCBI_PARAM_TYPE(CGI, Discard_UNK_SESSION) TParamDiscardUnkSession
CCgiApplication * m_App
Definition: cgictx.hpp:267
ERequestMethod
Standard request methods.
Definition: ncbicgi.hpp:891
T max(T x_, T y_)
const string & GetValue() const
Get the value as a string, (necessarily) prefetching it all if applicable; the result remains availab...
Definition: ncbicgi.hpp:462
static string & ToUpper(string &str)
Convert string to upper case – string& version.
Definition: ncbistr.cpp:317
Timeout structure.
Definition: ncbi_types.h:76
Magic spell ;-) needed for some weird compilers... very empiric.
int TStreamStatus
Definition: cgictx.hpp:234
const TCgiEntries & GetEntries(void) const
Get a set of entries(decoded) received from the client.
Definition: ncbicgi.hpp:1154
virtual ~CCgiServerContext(void)
Definition: cgictx.cpp:66
static bool s_IsAllowedMethod(const string &method)
Definition: cgictx.cpp:702
Process information in the NCBI Registry, including working with configuration files.
virtual ICgiSessionStorage * GetSessionStorage(CCgiSessionParameters &params) const
Get instance of CGI session storage interface.
Definition: cgiapp.cpp:1353
Definition: inftrees.h:24
void erase(iterator pos)
Definition: map.hpp:307
void SetCgiRequest(const CCgiRequest &request)
Definition: ncbicgir.hpp:411
CCgiResponse m_Response
Definition: cgictx.hpp:269
void DisableTrackingCookie(void)
Definition: ncbicgir.cpp:553
const CNcbiRegistry & GetConfig(void) const
Get the application's cached configuration parameters (read-only).
Definition: ncbiapp.hpp:696
Defines NCBI C++ diagnostic APIs, classes, and macros.
NCBI
Definition: static_set.hpp:72
const CCgiEntry & GetEntry(const string &name, bool *is_found=0) const
Get entry value by name.
Definition: ncbicgi.cpp:1435
A session cookie will be added to the response.
Definition: cgi_session.hpp:83
unique_ptr< CCgiServerContext > m_ServerContext
Definition: cgictx.hpp:277
static const GLdouble origin[]
static uch flags
virtual CCgiServerContext * LoadServerContext(CCgiContext &context)
Definition: cgiapp.cpp:816
CCgiCookies::
Definition: ncbicgi.hpp:220
void SetStatus(unsigned int code, const string &reason=kEmptyStr)
Definition: ncbicgir.cpp:197
#define NCBI_EXCEPTION_THROW(exception_var)
Throw an existing exception object.
Definition: ncbiexpt.hpp:530
CCgiAppException –.
Case sensitive compare.
Definition: ncbistr.hpp:1191
virtual CNcbiOstream & Write(CNcbiOstream &os) const
Definition: cgictx.cpp:97
const GenericPointer< typename T::ValueType > T2 value
Definition: pointer.h:1185
Do not use per-thread values.
Definition: ncbi_param.hpp:502
static const char * kAC_AllowOrigin
Definition: cgictx.cpp:649
void CheckStatus(void) const
Check if the context has any pending errors, perform any required actions (e.g.
Definition: cgictx.cpp:562
NCBI_PARAM_DECL(bool, CGI, Discard_UNK_SESSION)
CNcbiRegistry –.
Definition: ncbireg.hpp:914
const string & GetValue(void) const
All "const string& GetXXX(...)" methods beneath return reference to "NcbiEmptyString" if the requeste...
Definition: ncbicgi.hpp:1013
ICgiSessionStorage –.
static CS_CONTEXT * ctx
Definition: ct_dynamic.c:24
static bool s_IsAllowedHeaderList(const string &headers)
Definition: cgictx.cpp:727
#define _ASSERT
string m_StatusMessage
Definition: cgictx.hpp:286
string s_HeaderToHttp(const char *name)
Definition: cgictx.cpp:763
void RemoveHeaderValue(const string &name)
Definition: ncbicgir.cpp:125
#define NCBI_EXCEPTION_VAR(name, exception_class, err_code, message)
Create an instance of the exception to be thrown later.
Definition: ncbiexpt.hpp:526
static bool EqualNocase(const CTempString str, SIZE_TYPE pos, SIZE_TYPE n, const char *pattern)
Case-insensitive equality of a substring with a pattern.
Definition: ncbistr.hpp:5087
Do not set outgoing tracking cookie.
Definition: ncbicgi.hpp:726
static const char * kAC_AllowMethods
Definition: cgictx.cpp:648
static string SelectLastSessionID(const string &session_ids)
Select the last session id from the list of ids separated with commas and optional spaces...
static bool EndsWith(const CTempString str, const CTempString end, ECase use_case=eCase)
Check if a string ends with a specified suffix value.
Definition: ncbistr.hpp:5157
#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:547
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:3362
string m_SessionCookieDomain
Definition: cgictx.hpp:346
#define const
Definition: zconf.h:217
Definition: dbpivot.c:60
static CRequestContext & GetRequestContext(void)
Shortcut to CDiagContextThreadData::GetThreadData().GetRequestContext()
Definition: ncbidiag.cpp:1730
CCgiException::EStatusCode m_StatusCode
Definition: cgictx.hpp:285
PNocase_Generic< string > PNocase
Definition: ncbistr.hpp:4690
void SetSessionID(const string &session)
static const char * prefix[]
Definition: pcregrep.c:251
static bool s_CheckRequestEntryForTID(const CCgiRequest *request, const string &entry_name, string &tid)
Definition: cgictx.cpp:474
string m_TrackingId
Definition: cgictx.hpp:280
static bool Equal(const CTempString str, SIZE_TYPE pos, SIZE_TYPE n, const char *pattern, ECase use_case=eCase)
Test for equality of a substring with a pattern.
Definition: ncbistr.hpp:5113
void x_InitSession(CCgiRequest::TFlags flags, ICgiSessionStorage *session_storage=nullptr)
Definition: cgictx.cpp:186
API to store CGI session data between Web requests.
ERequestMethod GetRequestMethod(void) const
Get request method.
Definition: ncbicgi.cpp:1796
CNcbiArguments –.
Definition: ncbienv.hpp:229
IO_PREFIX::istream CNcbiIstream
Portable alias for istream.
Definition: ncbistre.hpp:139
#define Type
static std::unique_ptr< CParams > params
Definition: wgs_params.cpp:491
#define BEGIN_NCBI_SCOPE
Define ncbi namespace.
Definition: ncbistl.hpp:98
static int result
Definition: cursor5.c:11
string m_JQuery_Callback
Definition: ncbicgir.hpp:312
EStatusCode
HTTP status codes.
TStreamStatus GetStreamStatus(void) const
Definition: cgictx.hpp:454
T & Get(void)
Create the variable if not created yet, return the reference.
string RetrieveTrackingId() const
Definition: cgictx.cpp:499
A session cookie will not be added to the response.
Definition: cgi_session.hpp:84
const CCgiRequest & GetRequest(void) const
Definition: cgictx.hpp:374
Modified on Mon Feb 19 12:45:08 2018 by modify_doxy.py rev. 546573