1 // This may look like C code, but it's really -*- C++ -*-
2 /*
3  * Copyright (C) 2008 Emweb bv, Herent, Belgium.
4  *
5  * See the LICENSE file for terms of use.
6  */
7 #ifndef WT_WEB_CONTROLLER_H_
8 #define WT_WEB_CONTROLLER_H_
9 
10 #include <string>
11 #include <vector>
12 #include <set>
13 #include <map>
14 
15 #include <Wt/WDllDefs.h>
16 #include <Wt/WServer.h>
17 #include <Wt/WSocketNotifier.h>
18 
19 #include "EntryPoint.h"
20 #include "SocketNotifier.h"
21 
22 #if defined(WT_THREADED) && !defined(WT_TARGET_JAVA)
23 #include <thread>
24 #include <mutex>
25 #endif
26 
27 namespace http {
28   namespace server {
29 	class ProxyReply;
30   }
31 }
32 
33 namespace Wt {
34 
35 class Configuration;
36 class EntryPoint;
37 
38 class WebRequest;
39 class WebSession;
40 
41 class WApplication;
42 class WServer;
43 
44 #ifdef WT_CNOR
45 class Runnable {
46 public:
47   void run();
48 };
49 
50 typedef Runnable *Function;
51 
52 #define WT_CALL_FUNCTION(f) (f)->run();
53 
54 #else
55 typedef std::function<void ()>  Function;
56 #define WT_CALL_FUNCTION(f) (f)();
57 #endif
58 
59 /*
60  * An event to be delivered to a session which is not caused by a web
61  * request (or, probably not one for that session).
62  */
63 struct ApplicationEvent {
64   ApplicationEvent(const std::string& aSessionId,
65 		   const Function& aFunction,
66                    const Function& aFallbackFunction = Function())
sessionIdApplicationEvent67     : sessionId(aSessionId),
68       function(aFunction),
69       fallbackFunction(aFallbackFunction)
70   { }
71 
72   std::string sessionId;
73   Function function;
74   Function fallbackFunction;
75 };
76 
77 /*
78  * The controller handle incoming request, in handleRequest().
79  * Optionally, it will expire session on each incoming request. Seems
80  * harmless to me (but causes confusion to others).
81  *
82  * There is a method shutdown() to quit the controller.
83  *
84  * It has the following tasks:
85  *  - handle session life-cycle: create new sessions, delete quit()ed
86  *    sessions, expire sessions on timeout
87  *  - handles web requests and application events
88  */
89 class WT_API WebController
90 #ifdef WT_TARGET_JAVA
91   : public WServer
92 #endif // WT_TARGET_JAVA
93 {
94 public:
isAsyncSupported()95   static bool isAsyncSupported() { return true; }
96 
97   std::unique_ptr<WApplication> doCreateApplication(WebSession *session);
98   Configuration& configuration();
99 
100   void addSession(const std::shared_ptr<WebSession>& session);
101   void removeSession(const std::string& sessionId);
102   void sessionDeleted();
103 
104   void newAjaxSession();
105   bool limitPlainHtmlSessions();
server()106   WServer *server() { return &server_; }
107 
108   std::string computeRedirectHash(const std::string& url);
109 
110 #ifdef WT_TARGET_JAVA
111   int getIdForWebSocket();
112   std::string getContextPath();
113 #else // WT_TARGET_JAVA
114   WebController(WServer& server,
115 		const std::string& singleSessionId = std::string(),
116 		bool autoExpire = true);
117   ~WebController();
118 
119   int sessionCount() const;
120 
121   // Returns whether we should continue receiving data.
122   bool requestDataReceived(WebRequest *request, std::uintmax_t current,
123 			   std::uintmax_t total);
124 
125   void handleRequest(WebRequest *request);
126 
127 #ifndef WT_CNOR
128   bool handleApplicationEvent(const std::shared_ptr<ApplicationEvent>& event);
129 #endif // WT_CNOR
130 
131   std::vector<std::string> sessions(bool onlyRendered = false);
132   bool expireSessions();
133   void start();
134   void shutdown();
135 
136   static std::string sessionFromCookie(const char * const cookies,
137 				       const std::string& scriptName,
138                                        const int sessionIdLength);
139 
140   typedef std::map<int, WSocketNotifier *> SocketNotifierMap;
141 
142   void addSocketNotifier(WSocketNotifier *notifier);
143   void removeSocketNotifier(WSocketNotifier *notifier);
144 
145   void addUploadProgressUrl(const std::string& url);
146   void removeUploadProgressUrl(const std::string& url);
147 
148   // returns false if removeSocketNotifier was called while processing
149   void socketSelected(int descriptor, WSocketNotifier::Type type);
150 
151   std::string switchSession(WebSession *session,
152 			    const std::string& newSessionId);
153   std::string generateNewSessionId(const std::shared_ptr<WebSession>& session);
154 
155 private:
156   Configuration& conf_;
157   std::string singleSessionId_;
158   bool autoExpire_;
159   int plainHtmlSessions_, ajaxSessions_;
160   volatile int zombieSessions_;
161   std::string redirectSecret_;
162   bool running_;
163 
164 #ifdef WT_THREADED
165   std::mutex uploadProgressUrlsMutex_;
166 #endif // WT_THREADED
167   std::set<std::string> uploadProgressUrls_;
168 
169   typedef std::map<std::string, std::shared_ptr<WebSession> > SessionMap;
170   SessionMap sessions_;
171 
172 #ifdef WT_THREADED
173   // mutex to protect access to the sessions map and plain/ajax session
174   // counts
175   mutable std::recursive_mutex mutex_;
176 
177   SocketNotifier socketNotifier_;
178   // mutex to protect access to notifier maps. This cannot be protected
179   // by mutex_ as this lock is grabbed while the application lock is
180   // being held, which would potentially deadlock if we took mutex_.
181   std::recursive_mutex notifierMutex_;
182   SocketNotifierMap socketNotifiersRead_;
183   SocketNotifierMap socketNotifiersWrite_;
184   SocketNotifierMap socketNotifiersExcept_;
185   // assumes that you did grab the notifierMutex_
186   SocketNotifierMap& socketNotifiers(WSocketNotifier::Type type);
187   void socketNotify(int descriptor, WSocketNotifier::Type type);
188 #endif
189 
190   struct UpdateResourceProgressParams {
191       std::string requestParam;
192       std::string resourceParam;
193       ::int64_t postDataExceeded;
194       std::string pathInfo;
195       std::uintmax_t current;
196       std::uintmax_t total;
197   };
198   void updateResourceProgress(const UpdateResourceProgressParams &params);
199 
200   EntryPointMatch getEntryPoint(WebRequest *request);
201 
202   static std::string appSessionCookie(const std::string& url);
203 
204 #endif // WT_TARGET_JAVA
205 
206   WServer& server_;
207 
208   friend class http::server::ProxyReply;
209   friend class WEnvironment;
210 };
211 
212 }
213 
214 #endif // WT_WEB_CONTROLLER_H_
215