HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
NET_WebServer.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: NET_WebServer.h
7  *
8  * COMMENTS:
9  *
10  */
11 
12 #ifndef __NET_WEBSERVER_H__
13 #define __NET_WEBSERVER_H__
14 
15 #include "NET_API.h"
16 
17 #include "NET_HTTPRequest.h"
18 #include "NET_HttpIO.h"
19 #include "NET_UrlHandler.h"
20 #include "NET_WebEndpoint.h"
21 #include "NET_WebMiddleware.h"
22 #include "NET_WebScheduler.h"
23 #include "NET_WebServerSettings.h"
24 #include "NET_WebStatsManager.h"
25 #include "NET_WebTaskHandler.h"
26 #include "NET_WebWorkerThread.h"
27 #include "NET_WebAPI.h"
28 #include "NET_Error.h"
29 #include "NET_RadixTree.h"
30 #include "NET_WebSocketAPIRouter.h"
31 
32 #include <UT/UT_ArrayStringMap.h>
33 #include <UT/UT_Assert.h>
34 #include <UT/UT_Condition.h>
35 #include <UT/UT_Functor.h>
36 #include <UT/UT_NetSocket.h>
37 #include <UT/UT_Optional.h>
38 #include <UT/UT_SharedPtr.h>
39 #include <UT/UT_Signal.h>
40 #include <UT/UT_StringHolder.h>
41 #include <UT/UT_Thread.h>
42 #include <UT/UT_UniquePtr.h>
43 #include <UT/UT_ValArray.h>
44 #include <UT/UT_Url.h>
45 
46 #include <SYS/SYS_AtomicInt.h>
47 #include <SYS/SYS_Compiler.h>
48 
49 #include <utility>
50 
51 #define NET_LOG_EMERG 0
52 #define NET_LOG_ALERT 1
53 #define NET_LOG_CRIT 2
54 #define NET_LOG_ERR 3
55 #define NET_LOG_WARNING 4
56 #define NET_LOG_NOTICE 5
57 #define NET_LOG_INFO 6
58 #define NET_LOG_DEBUG 7
59 #define NET_LOG_CONS 0x02
60 
61 #ifndef WIN32
62 #include <syslog.h>
63 #define NET_SYSLOG(ZZ) ::syslog ZZ
64 #else
65 #include <UT/UT_Format.h>
66 #define NET_SYSLOG(ZZ) ::fsSysLog ZZ
67 
68 template <typename... Args>
69 static void
70 fsSysLog(int priority, const char *fmt, Args &&... args)
71 {
72  UTprintf(stderr, fmt, std::forward<Args>(args)...);
73 }
74 #endif
75 
76 class NET_WebEndpoint;
79 
82 
83 /// @brief Holds information about the port. In HTTP case this will hold any
84 /// handlers that are bound to that port.
85 class NET_API NET_PortInformation
86 {
87 public:
88  NET_PortInformation() : myPort(-1) {}
89 
90  virtual ~NET_PortInformation() = default;
91 
92  void setPort(int port)
93  {
94  myPort = port;
95  }
97  {
98  return myPort;
99  }
101  {
102  return myPort > 0;
103  }
104  bool handle(NET_HTTPWebTaskHandler* handler);
105  bool handleWebSocket(NET_WebSocketHandler* handler,
106  const UT_StringHolder& msg);
107  void routeRequest(
108  NET_HTTPRequest &req,
109  NET_WebResponse &resp) const;
110  void insertHandler(const NET_UrlHandlerPtr& handler);
111 
112  NET_WebSocketAPIRouter& webSocketRouter() { return myWebSocketRouter; }
113  static bool isOriginAccepted(
114  const UT_StringArray &whitelist,
115  const UT_Url &origin);
116 
117  // A list of strings that match Origins that are authorized to make
118  // cross-site HTTP requests.
119  // NOTE: These can be regex or not.
121 private:
122  void shutdown_(UT_NetFDSet& fds);
123  bool bindPort_();
124  int fd_() const
125  {
126  if (!myServerSocket)
127  return -1;
128  return myServerSocket->getSocket();
129  }
130 
131  friend class NET_WebServer;
132 
134 
135  int myPort;
136 
137  // The paths and the associated ports the server app will serve.
139 
140  // TODO: this shouldnt be assumed that everyone wants a websocket router.
141  // Instead allow a adding and removing components to the app. Something like
142  // a factory per app or child apps.
143  // The websockets api router
144  NET_WebSocketAPIRouter myWebSocketRouter;
145 
146  // Additions and removals are only ever done on the main thread!!!
148  NET_Socket myServerSocket;
149 };
150 
152 
153 class NET_API NET_WebServer
154 {
155 public:
156  using ServerCallback = std::function<void()>;
157  using SignalCallback = std::function<void(UTsignalHandlerArg)>;
158 
159  NET_WebServer(const NET_WebServer &) = delete;
160  NET_WebServer &operator=(const NET_WebServer &) = delete;
161 
162  static NET_WebServer &instance();
163 
164  NET_WebServerSettings &settings() { return mySettings; }
165 
166  // Send a response back to the client using the middle ware. This
167  // function should only ever be used by the server and in unique
168  // cases where the request is invalid but still needs to respond
169  template <typename... Args>
170  static bool sendResponse(
171  NET_WebEndpoint &ep,
172  NET_HTTPRequest &req,
173  NET_HTTPStatusCode code,
174  Args &&... args)
175  {
176  NET_WebResponse resp(
177  createResponse(req, code, std::forward<Args>(args)...));
178 
179  return sendResponse(ep, req, resp);
180  }
181  static bool sendResponse(
182  NET_WebEndpoint &ep,
183  NET_HTTPRequest &req,
184  NET_WebResponse &resp)
185  {
186  // Allow middleware to perform various actions on the request
187  instance().myMiddleware.processResponse(req, resp);
188  return NET_HttpIO::write(ep, resp);
189  }
190 
191  /// @brief Handle an http request
192  ///
193  /// @param handler The handler that we want the server to execute on
194  ///
195  /// @return True if we want the connection closed for some reason.
196  bool handle(NET_HTTPWebTaskHandler* handler);
197  /// @brief Handle an api websocket request
198  ///
199  /// @param handler The handler that we want the server to execute on
200  ///
201  /// @return True if we want the connection closed for some reason.
202  bool handleWebSocket(
203  NET_WebSocketHandler *handler,
204  const UT_StringHolder &msg);
205 
206  static NET_WebResponse defaultOptionsRouteCallback(NET_HTTPRequest &req);
207 
208  // This allows the web server to configure the request before its handled.
209  // (i.e. run it through the middleware)
210  static void configureRequest(NET_HTTPRequest &req);
212  {
213  myMiddleware.add(mw);
214  }
216  {
217  myMiddleware.add(mw);
218  }
219 
220  void requestPortShutdown(int port);
221  /// @brief Request that all server apps shutdown.
222  SYS_FORCE_INLINE void requestFullShutdown() { myIsQuitting = true; }
223  /// @brief Shutdown the webserver immediately.
224  void shutdown();
225 
226  void addShutdownCallback(ServerCallback clb);
227 
228  const UT_StringHolder &program() const { return mySettings.myProgramName; }
229 
230  void configureThreadFarm(int max_threads);
231 
232  bool hasRequestedQuitting() const { return myIsQuitting; }
233 
234  void queueEndpoint(NET_WebEndpointPtr ep);
235 
236  // Set the task factory. This is required so that the correct task type
237  // is being created.
238  void setTaskFactory(UT_UniquePtr<NET_WebTaskFactory> factory);
239 
240  /// @brief Run the server with the provided port information.
241  ///
242  /// @param port_info The port we would like to run the server with.
243  ///
244  /// @return True if running the server was a success. This returns once
245  /// the server is completely finished running.
246  bool run(const NET_PortInformationSPtr& port_info, bool is_single_instance);
247  bool run(const UT_Map<int, NET_PortInformationSPtr>& port_infos, bool is_single_instance);
248  /// @brief Reload the server. This adds the new port to the server. This will
249  /// return once its added the port to the queue.
250  ///
251  /// @param port_info The port_info we would like to add into the already
252  /// running server.
253  ///
254  /// @return True if the reload operation worked. This returns immediately.
255  bool reload(const NET_PortInformationSPtr& port_info);
256  bool reload(const UT_Map<int, NET_PortInformationSPtr> &port_infos);
257 
258  // Check that we have read/write failure and log an error if we do not
259  // @param log_error denotes if we should log an error if we fail the check.
260  bool checkReadIp(const NET_WebEndpoint &ep, bool log_error = true) const;
261  bool checkWriteIp(const NET_WebEndpoint &ep, bool log_error = true) const;
262  bool checkReadIp(const NET_HTTPRequest &req, bool log_error = true) const;
263  bool checkWriteIp(const NET_HTTPRequest &req, bool log_error = true) const;
264  bool checkReadIp(const unsigned short cip[4], bool log_error = true) const;
265  bool checkWriteIp(const unsigned short cip[4], bool log_error = true) const;
266 
267  static bool isLocal(const NET_WebEndpoint &ep);
268  static bool isLocal(const NET_HTTPRequest &req);
269  static bool isLocal(const unsigned short ip[4]);
270 
271  static void serverIp(unsigned short ip[4]);
272  static UT_StringHolder serverIpStr();
273 
274  void setTerminateCallback(SignalCallback clb) { myTerminateCallback = clb; }
275 
276  void addKeepAliveEndpoint(NET_WebEndpointPtr keep_alive);
277 
278  NET_WebScheduler &scheduler() { return myScheduler; }
279 
280  static constexpr UT_StringLit releaseLabel()
281  {
282 #if UT_ASSERT_LEVEL > 0
283  return "development";
284 #else
285  return "release";
286 #endif
287  }
288 
289  // Create a specific handler by the protocol id.
290  NET_WebTaskHandlerPtr createHandler(
291  NET_WebEndpoint &ep,
293  const char *path = nullptr) const;
295  {
296  if (!myTaskFactory)
297  return nullptr;
298  return myTaskFactory->create(ep);
299  }
302  const char *path = nullptr) const
303  {
304  if (!myTaskFactory)
305  return false;
306  return myTaskFactory->supportsProtocol(id, path);
307  }
309  {
310  if (!myTaskFactory)
311  return false;
312  return myTaskFactory->supportsProtocol(ep);
313  }
314 
315  NET_WebStatsManager &statsManager() { return myStatsManager; }
316 
317  void endpointCreated() { myTaskCount.add(1); }
318  void endpointDestroyed() { myTaskCount.add(-1); }
319  int32 currentTaskCount() const { return myTaskCount.load(); }
320 private:
321  NET_WebServer();
322 
323  // Run the main loop for the web server. This blocks until we are finished.
324  bool runServerLoop();
325 
326  // Create web endpoint
327  NET_WebEndpointPtr createEndpoint(
328  const NET_PortInformationSPtr &port_info,
330 
331  // Handle regular terminate signals
332  static void handleTerminate(UTsignalHandlerArg arg);
333  // Handle fatal terminate signals
334  static void handleFatal(UTsignalHandlerArg arg);
335  static void handleExit(void *);
336 
337  // Accept an incoming connection.
338  bool acceptConnection(const NET_PortInformationSPtr &app_info, int &status);
339 
340  // Process the event using the ep and the event info.
341  void processEvent(
343  const UT_NetFDSet::EventInfo &event_info);
344 
345  static NET_WebResponse createResponse(
346  NET_HTTPRequest &req,
347  NET_HTTPStatusCode code,
349  const UT_WorkBuffer &body);
350  // This assumes that the body is of text format.
351  static NET_WebResponse createResponse(
352  NET_HTTPRequest &req,
353  NET_HTTPStatusCode code,
354  const UT_WorkBuffer &body);
355  static NET_WebResponse createResponse(
356  NET_HTTPRequest &req,
357  NET_HTTPStatusCode code,
359  const UT_StringHolder &body);
360  // This assumes that the body is of text format.
361  static NET_WebResponse createResponse(
362  NET_HTTPRequest &req,
363  NET_HTTPStatusCode code,
364  const UT_StringHolder &body);
365  static NET_WebResponse createResponse(
366  NET_HTTPRequest &req,
367  NET_HTTPStatusCode code);
368 
369  void internalServerLoop(UT_NetFDSet &fds, bool use_fast_fd_set);
370 
371  NET_PortInformationSPtr getPortInfo(int port);
372 
374  UT_Array<NET_PortInformationSPtr> myQueuedPorts;
375  UT_Array<int> myQueuedPortsShutdowns;
376 
377  NET_WebMiddleware myMiddleware;
378  UT_Lock myNewEndpointLock;
379  UT_Array<NET_WebEndpointPtr> myPendingNewEndpoints;
380 
381  UT_UniquePtr<UT_ThreadFarm> myThreadFarm;
382 
383  UT_UniquePtr<NET_WebTaskFactory> myTaskFactory;
384  NET_WebServerSettings mySettings;
385  UT_Array<ServerCallback> myShutdownCallbacks;
386  SignalCallback myTerminateCallback;
387 
388  unsigned char myReload : 1, myIsQuitting : 1, myHasAddedExitCallback : 1,
389  myHasShutdown : 1, myIsRunning : 1;
390 
391  SYS_AtomicInt<int32> myTaskCount;
392 
393  NET_WebStatsManager myStatsManager;
394 
395  exint myNextWorkerIdx;
397 
398  UT_Lock myReloadLock;
399  UT_Lock myStackTraceLock;
400 #ifdef LINUX
401  UT_Lock myPidLock;
402 #endif
403 
404  NET_WebScheduler myScheduler;
405 };
406 
407 #endif // __NET_WEBSERVER_H__
408 
static constexpr UT_StringLit releaseLabel()
std::function< void(NET_HTTPRequest &)> RequestMiddleware
SYS_NO_DISCARD_RESULT bool isValid() const
std::function< void()> ServerCallback
int int32
Definition: SYS_Types.h:39
const UT_StringHolder & program() const
Response object used for responding to request in the server.
Abstracts the socket file descriptor set for select() or poll() calls.
Definition: UT_NetFDSet.h:34
A simple radix tree implementation.
Definition: NET_RadixTree.h:32
Holds information about the port. In HTTP case this will hold any handlers that are bound to that por...
Definition: NET_WebServer.h:85
UT_SharedPtr< NET_BaseUrlHandler > NET_UrlHandlerPtr
Definition: NET_WebServer.h:80
void endpointCreated()
UT_SharedPtr< NET_WebEndpoint > NET_WebEndpointPtr
const Args & args
Definition: printf.h:628
internal::named_arg< T, char > arg(string_view name, const T &arg)
Definition: core.h:1393
UT_UniquePtr< NET_WebTaskHandler > NET_WebTaskHandlerPtr
NET_WebScheduler & scheduler()
int64 exint
Definition: SYS_Types.h:125
int32 currentTaskCount() const
bool supportsProtocol(NET_WebTaskFactory::ProtocolId id, const char *path=nullptr) const
NET_WebServerSettings & settings()
NET_HTTPStatusCode
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
Definition: UT_UniquePtr.h:33
void endpointDestroyed()
SYS_FORCE_INLINE void requestFullShutdown()
Request that all server apps shutdown.
std::function< void(UTsignalHandlerArg)> SignalCallback
NET_WebTaskHandlerPtr createHandler(NET_WebEndpoint &ep) const
std::shared_ptr< T > UT_SharedPtr
Wrapper around std::shared_ptr.
Definition: UT_SharedPtr.h:28
This is a generic handler for serving only http requests.
#define SYS_FORCE_INLINE
Definition: SYS_Inline.h:45
UT_StringArray myOriginWhitelist
Definition: UT_Url.h:22
void addMiddleware(NET_WebMiddleware::RequestMiddleware mw)
bool hasRequestedQuitting() const
#define SYS_NO_DISCARD_RESULT
Definition: SYS_Compiler.h:93
Simple holder for statistics relating to the web server.
void setPort(int port)
Definition: NET_WebServer.h:92
void setTerminateCallback(SignalCallback clb)
UT_UniquePtr< UT_NetSocket > NET_Socket
Definition: NET_WebServer.h:81
size_t UTprintf(FILE *file, const char *format, const Args &...args)
GLsizei const GLchar *const * path
Definition: glew.h:6461
NET_WebStatsManager & statsManager()
NET_WebSocketAPIRouter & webSocketRouter()
UT_SharedPtr< NET_PortInformation > NET_PortInformationSPtr
SYS_NO_DISCARD_RESULT int port() const
Definition: NET_WebServer.h:96
void addMiddleware(NET_WebMiddleware::ResponseMiddleware mw)
Type-safe formatting, modeled on the Python str.format function.
bool supportsProtocol(NET_WebEndpoint &ep) const
static bool sendResponse(NET_WebEndpoint &ep, NET_HTTPRequest &req, NET_HTTPStatusCode code, Args &&...args)
static bool sendResponse(NET_WebEndpoint &ep, NET_HTTPRequest &req, NET_WebResponse &resp)
static bool write(NET_WebEndpoint &ep, NET_HTTPStatusCode code)
std::function< void(NET_HTTPRequest &, NET_WebResponse &)> ResponseMiddleware