1 #ifndef NCBI_CGI_CTX__HPP
2 #define NCBI_CGI_CTX__HPP
3
4 /* $Id: cgictx.hpp 540924 2017-07-12 15:06:18Z grichenk $
5 * ===========================================================================
6 *
7 * PUBLIC DOMAIN NOTICE
8 * National Center for Biotechnology Information
9 *
10 * This software/database is a "United States Government Work" under the
11 * terms of the United States Copyright Act. It was written as part of
12 * the author's official duties as a United States Government employee and
13 * thus cannot be copyrighted. This software/database is freely available
14 * to the public for use. The National Library of Medicine and the U.S.
15 * Government have not placed any restriction on its use or reproduction.
16 *
17 * Although all reasonable efforts have been taken to ensure the accuracy
18 * and reliability of the software and data, the NLM and the U.S.
19 * Government do not and cannot warrant the performance or results that
20 * may be obtained by using this software or data. The NLM and the U.S.
21 * Government disclaim all warranties, express or implied, including
22 * warranties of performance, merchantability or fitness for any particular
23 * purpose.
24 *
25 * Please cite the author in any work or product based on this material.
26 *
27 * ===========================================================================
28 *
29 * Author:
30 * Vsevolod Sandomirskiy
31 *
32 * File Description:
33 * Basic CGI Application class
34 */
35
36 #include <cgi/cgiapp.hpp>
37 #include <cgi/cgi_session.hpp>
38 #include <connect/ncbi_types.h>
39 #include <cgi/cgi_exception.hpp>
40
41
42 /** @addtogroup CGIBase
43 *
44 * @{
45 */
46
47
48 BEGIN_NCBI_SCOPE
49
50 class CUrl;
51
52 /////////////////////////////////////////////////////////////////////////////
53 //
54 // CCgiServerContext::
55 //
56
57 class NCBI_XCGI_EXPORT CCgiServerContext
58 {
59 public:
60 virtual ~CCgiServerContext(void);
61 };
62
63
64
65 /////////////////////////////////////////////////////////////////////////////
66 //
67 // CCtxMsg::
68 //
69
70 class NCBI_XCGI_EXPORT CCtxMsg
71 {
72 public:
73 virtual ~CCtxMsg(void);
74 virtual CNcbiOstream& Write(CNcbiOstream& os) const = 0;
75 };
76
77
78 /* @} */
79
80
81 inline
operator <<(CNcbiOstream & os,const CCtxMsg & ctx_msg)82 CNcbiOstream& operator<< (CNcbiOstream& os, const CCtxMsg& ctx_msg)
83 {
84 return ctx_msg.Write(os);
85 }
86
87
88 /** @addtogroup CGIBase
89 *
90 * @{
91 */
92
93
94 /////////////////////////////////////////////////////////////////////////////
95 //
96 // CCtxMsgString::
97 //
98
99 class NCBI_XCGI_EXPORT CCtxMsgString : public CCtxMsg
100 {
101 public:
CCtxMsgString(const string & msg)102 CCtxMsgString(const string& msg) : m_Message(msg) {}
103 virtual ~CCtxMsgString(void);
104 virtual CNcbiOstream& Write(CNcbiOstream& os) const;
105
106 static const char* sm_nl;
107
108 private:
109 string m_Message;
110 };
111
112
113 /////////////////////////////////////////////////////////////////////////////
114 //
115 // CCgiContext::
116 //
117 // CCgiContext is a wrapper for request, response, server context.
118 // In addition, it contains list of messages (as HTML nodes).
119 // Having non-const reference, CCgiContext's user has access to all its
120 // internal data.
121 // Context will try to create request from given data or default request
122 // on any request creation error
123 //
124
125 class CNcbiEnvironment;
126 class CNcbiRegistry;
127 class CNcbiResource;
128 class CCgiApplication;
129
130
131 class NCBI_XCGI_EXPORT CCgiContext
132 {
133 public:
134 CCgiContext(CCgiApplication& app,
135 const CNcbiArguments* args = 0 /* D: app.GetArguments() */,
136 const CNcbiEnvironment* env = 0 /* D: app.GetEnvironment() */,
137 CNcbiIstream* inp = 0 /* see ::CCgiRequest(istr) */,
138 CNcbiOstream* out = 0 /* see ::CCgiResponse(out) */,
139 int ifd = -1,
140 int ofd = -1,
141 size_t errbuf_size = 256, /* see CCgiRequest */
142 CCgiRequest::TFlags flags = 0
143 );
144
145 CCgiContext(CCgiApplication& app,
146 CNcbiIstream* inp /* see ::CCgiRequest(istr) */,
147 CNcbiOstream* out /* see ::CCgiResponse(out) */,
148 CCgiRequest::TFlags flags = 0
149 );
150
151 CCgiContext(ICgiSessionStorage* session_storage,
152 const CNcbiArguments* args = 0 /* D: app.GetArguments() */,
153 const CNcbiEnvironment* env = 0 /* D: app.GetEnvironment() */,
154 CNcbiIstream* inp = 0 /* see ::CCgiRequest(istr) */,
155 CNcbiOstream* out = 0 /* see ::CCgiResponse(out) */,
156 int ifd = -1,
157 int ofd = -1,
158 size_t errbuf_size = 256, /* see CCgiRequest */
159 CCgiRequest::TFlags flags = 0
160 );
161
162 virtual ~CCgiContext(void);
163
164 const CCgiApplication& GetApp(void) const;
165
166 const CNcbiRegistry& GetConfig(void) const;
167 CNcbiRegistry& GetConfig(void);
168
169 // these methods will throw exception if no server context is set
170 const CNcbiResource& GetResource(void) const;
171 CNcbiResource& GetResource(void);
172
173 const CCgiRequest& GetRequest(void) const;
174 CCgiRequest& GetRequest(void);
175
176 const CCgiResponse& GetResponse(void) const;
177 CCgiResponse& GetResponse(void);
178
179 // these methods will throw exception if no server context set
180 const CCgiServerContext& GetServCtx(void) const;
181 CCgiServerContext& GetServCtx(void);
182
183 // message buffer functions
184 CNcbiOstream& PrintMsg(CNcbiOstream& os);
185
186 void PutMsg(const string& msg);
187 void PutMsg(CCtxMsg* msg);
188
189 bool EmptyMsg(void);
190 void ClearMsg(void);
191
192 // request access wrappers
193
194 // return entry from request
195 // return empty string if no such entry
196 // throw runtime_error if there are several entries with the same name
197 const CCgiEntry& GetRequestValue(const string& name, bool* is_found = 0)
198 const;
199
200 void AddRequestValue (const string& name, const CCgiEntry& value);
201 void RemoveRequestValues(const string& name);
202 void ReplaceRequestValue(const string& name, const CCgiEntry& value);
203
204 /// Whether to use the port number when composing the CGI's own URL
205 /// @sa GetSelfURL()
206 /// @deprecated The flag is ignored, use GetSelfURL(void).
207 enum ESelfUrlPort {
208 eSelfUrlPort_Use, ///< Use port number in self-URL
209 eSelfUrlPort_Strip, ///< Do not use port number in self-URL
210 eSelfUrlPort_Default ///< Use port number, except for NCBI front-ends
211 };
212
213 /// Using HTTP environment variables, compose the CGI's own URL as:
214 /// SCHEME://SERVER_NAME[:SERVER_PORT]/SCRIPT_NAME
215 /// @deprecated The flag is ignored, use GetSelfURL(void).
216 NCBI_DEPRECATED
GetSelfURL(ESelfUrlPort) const217 const string& GetSelfURL(ESelfUrlPort /*use_port*/) const
218 { return GetSelfURL(); }
219
220 /// Using HTTP environment variables, compose the CGI's own URL as:
221 /// SCHEME://SERVER_NAME[:SERVER_PORT]/SCRIPT_NAME
222 /// Port is always included if it does not correspond to the scheme's
223 /// default port.
224 const string& GetSelfURL(void) const;
225
226 /// Check if the current scheme is secure (https) or not (http).
227 bool IsSecure(void) const;
228
229 // Which streams are ready?
230 enum EStreamStatus {
231 fInputReady = 0x1,
232 fOutputReady = 0x2
233 };
234 typedef int TStreamStatus; // binary OR of 'EStreamStatus'
235 TStreamStatus GetStreamStatus(STimeout* timeout) const;
236 TStreamStatus GetStreamStatus(void) const; // supplies {0,0}
237
238 string RetrieveTrackingId() const;
239
240 /// Check if the context has any pending errors, perform any required actions
241 /// (e.g. throw an exception).
242 void CheckStatus(void) const;
243
244 /// Process cross-origin resource sharing (CORS) request.
245 /// If the request is a preflight one, send back the required data
246 /// and return true (in this case normal ProcessRequest should not
247 /// be called). Otherwise check if CORS is enabled and add the
248 /// required headers to the response.
249 static bool ProcessCORSRequest(const CCgiRequest& request,
250 CCgiResponse& response);
251
252 private:
253 CCgiApplication& x_GetApp(void) const;
254 CCgiServerContext& x_GetServerContext(void) const;
255 void x_InitSession(CCgiRequest::TFlags flags,
256 ICgiSessionStorage* session_storage = nullptr);
257
258 void x_SetStatus(CCgiException::EStatusCode code, const string& msg) const;
259
260 // Secure protocol flag.
261 enum ESecureMode {
262 eSecure_NotSet,
263 eSecure_Off,
264 eSecure_On
265 };
266
267 CCgiApplication* m_App;
268 unique_ptr<CCgiRequest> m_Request; // CGI request information
269 CCgiResponse m_Response; // CGI response information
270 unique_ptr<CCgiSession> m_Session; // CGI session
271
272 // message buffer
273 typedef list< AutoPtr<CCtxMsg> > TMessages;
274 TMessages m_Messages;
275
276 // server context will be obtained from CCgiApp::LoadServerContext()
277 unique_ptr<CCgiServerContext> m_ServerContext; // application defined context
278
279 mutable string m_SelfURL;
280 mutable string m_TrackingId; // cached tracking id
281 mutable ESecureMode m_SecureMode;
282
283 // Request status code and message. The status is non-zero if there
284 // is an error to report.
285 mutable CCgiException::EStatusCode m_StatusCode;
286 mutable string m_StatusMessage;
287
288 // forbidden
289 CCgiContext(const CCgiContext&);
290 CCgiContext& operator=(const CCgiContext&);
291 };
292
293
294 /////////////////////////////////////////////////////////////////////////////
295 //
296 // CCgiSessionParameters:
297 // This class is used to pass additional optional parameters from a CGI
298 // application to CGI session
299
300 class CCgiSessionParameters
301 {
302 public:
303
304 /// Spescify which class is responsible for Session Storage destruction
305 /// if set to eTakeOwnership, then a CGI session will delete it, otherwise
306 /// the CGI application should do it.
307 /// Default the CGI session takes responsibility.
SetImplOwnership(EOwnership owner)308 void SetImplOwnership(EOwnership owner) { m_ImplOwner = owner; }
309
310 /// Do not use a cookie to transfer session id between requests
311 /// By default cookie is enabled
DisableCookie()312 void DisableCookie() { m_CookieEnabled = false; }
313
314 /// Set name of the cookie with session id.
315 /// Default: ncbi_sessionid
SetSessionIdName(const string & name)316 void SetSessionIdName(const string& name) { m_SessionIdName = name; }
317
318 /// Set session cookie's domain
319 /// Default: .ncbi.nlm.nih.gov
SetSessionCookieDomain(const string & domain)320 void SetSessionCookieDomain(const string& domain)
321 { m_SessionCookieDomain = domain; }
322
323 /// Set session cookie's path
324 /// Default: /
SetSessionCookiePath(const string & path)325 void SetSessionCookiePath(const string& path)
326 { m_SessionCookiePath = path; }
327
328 /// Set session cookie's expiration time
329 /// Default: none
SetSessionCookieExpTime(const CTime & exp_time)330 void SetSessionCookieExpTime(const CTime& exp_time)
331 { m_SessionCookieExpTime = exp_time; }
332
333 private:
334
335 // Only CgiContext can create an instance of this class
336 friend class CCgiContext;
CCgiSessionParameters()337 CCgiSessionParameters() :
338 m_ImplOwner(eTakeOwnership), m_CookieEnabled(true),
339 m_SessionIdName(CCgiSession::kDefaultSessionIdName),
340 m_SessionCookieDomain(CCgiSession::kDefaultSessionCookieDomain),
341 m_SessionCookiePath(CCgiSession::kDefaultSessionCookiePath) {}
342
343 EOwnership m_ImplOwner;
344 bool m_CookieEnabled;
345 string m_SessionIdName;
346 string m_SessionCookieDomain;
347 string m_SessionCookiePath;
348 CTime m_SessionCookieExpTime;
349 };
350
351 /* @} */
352
353
354 /////////////////////////////////////////////////////////////////////////////
355
356 /////////////////////////////////////////////////////////////////////////////
357 // IMPLEMENTATION of INLINE functions
358 /////////////////////////////////////////////////////////////////////////////
359
360
361
362 /////////////////////////////////////////////////////////////////////////////
363 // CCgiContext::
364 //
365
366 inline
GetApp(void) const367 const CCgiApplication& CCgiContext::GetApp(void) const
368 {
369 return x_GetApp();
370 }
371
372
373 inline
GetRequest(void) const374 const CCgiRequest& CCgiContext::GetRequest(void) const
375 {
376 return *m_Request;
377 }
378
379
380 inline
GetRequest(void)381 CCgiRequest& CCgiContext::GetRequest(void)
382 {
383 return *m_Request;
384 }
385
386
387 inline
GetResponse(void) const388 const CCgiResponse& CCgiContext::GetResponse(void) const
389 {
390 return m_Response;
391 }
392
393
394 inline
GetResponse(void)395 CCgiResponse& CCgiContext::GetResponse(void)
396 {
397 return m_Response;
398 }
399
400
401 inline
GetServCtx(void) const402 const CCgiServerContext& CCgiContext::GetServCtx(void) const
403 {
404 return x_GetServerContext();
405 }
406
407
408 inline
GetServCtx(void)409 CCgiServerContext& CCgiContext::GetServCtx(void)
410 {
411 return x_GetServerContext();
412 }
413
414
415 inline
PrintMsg(CNcbiOstream & os)416 CNcbiOstream& CCgiContext::PrintMsg(CNcbiOstream& os)
417 {
418 ITERATE (TMessages, it, m_Messages) {
419 os << **it;
420 }
421 return os;
422 }
423
424
425 inline
PutMsg(const string & msg)426 void CCgiContext::PutMsg(const string& msg)
427 {
428 m_Messages.push_back(new CCtxMsgString(msg));
429 }
430
431
432 inline
PutMsg(CCtxMsg * msg)433 void CCgiContext::PutMsg(CCtxMsg* msg)
434 {
435 m_Messages.push_back(msg);
436 }
437
438
439 inline
EmptyMsg(void)440 bool CCgiContext::EmptyMsg(void)
441 {
442 return m_Messages.empty();
443 }
444
445
446 inline
ClearMsg(void)447 void CCgiContext::ClearMsg(void)
448 {
449 m_Messages.clear();
450 }
451
452
453 inline
GetStreamStatus(void) const454 CCgiContext::TStreamStatus CCgiContext::GetStreamStatus(void) const
455 {
456 STimeout timeout = {0, 0};
457 return GetStreamStatus(&timeout);
458 }
459
460
461 END_NCBI_SCOPE
462
463 #endif // NCBI_CGI_CTX__HPP
464