https://mooseframework.inl.gov
WebServerControl.h
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #pragma once
11 
12 #include "Control.h"
13 
14 #include <atomic>
15 #include <memory>
16 #include <thread>
17 
19 
21 class HttpServer;
22 
27 class WebServerControl : public Control
28 {
29 public:
31 
34 
41 
42  virtual void execute() override final;
43 
45 
56  template <class T>
58  {
59  public:
60  ControlledValue(const std::string & name, const std::string & type)
62  {
63  }
64  ControlledValue(const std::string & name, const std::string & type, const T & value)
66  {
67  }
68 
70  using value_type = T;
71 
72  virtual void setControllableValue(WebServerControl & control) override final
73  {
74  control.comm().broadcast(_value);
75  control.setControllableValueByName<T>(name(), _value);
76  }
77 
78  private:
80  T _value;
81  };
82 
83 protected:
87  enum class RequestMethod
88  {
89  GET,
90  POST
91  };
92 
96  struct Request
97  {
98  Request() = default;
99 
103  bool hasJSON() const { return _json.has_value(); }
104 
108  const nlohmann::json & getJSON() const;
109 
113  void setJSON(const nlohmann::json & json, const Moose::PassKey<WebServerControl>)
114  {
115  _json = json;
116  }
117 
118  private:
120  std::optional<nlohmann::json> _json;
121  };
122 
126  struct Response
127  {
128  Response() = default;
129 
133  Response(const unsigned int status_code);
137  Response(const unsigned int status_code, const nlohmann::json & json);
138 
142  unsigned int getStatusCode() const { return _status_code; }
143 
147  bool hasJSON() const { return _json.has_value(); }
151  const nlohmann::json & getJSON() const;
152 
156  bool hasError() const { return _error.has_value(); }
160  const std::string & getError() const;
161 
162  protected:
166  void setError(const std::string & error) { _error = error; }
167 
168  private:
170  unsigned int _status_code = 0;
172  std::optional<nlohmann::json> _json;
174  std::optional<std::string> _error;
175  };
176 
180  struct ErrorResponse : public Response
181  {
182  ErrorResponse(const std::string & error, const unsigned int status_code = 400);
183  };
184 
189  {
191 
195  bool getRequireWaiting() const { return _require_waiting; }
200  {
202  }
203 
212  {
214  }
215 
219  const std::set<std::string> getRequiredJSONKeys() const { return _required_json_keys; }
223  void requireJSONKey(const std::string & key) { _required_json_keys.insert(key); }
227  void requireJSONKeys(std::initializer_list<std::string> && keys)
228  {
229  _required_json_keys.insert(keys);
230  }
231 
232  private:
238  std::set<std::string> _required_json_keys;
239  };
240 
248  template <RequestMethod method>
249  void addServerAction(const std::string & path,
250  std::function<Response(const Request &, WebServerControl &)> && action,
251  const ServerActionOptions & options = {});
252 
256  virtual void addServerActions() {};
257 
263  template <class value_T, class key_T>
264  static value_T convertJSON(const nlohmann::json & json_value, const key_T & key);
265 
269  bool isCurrentlyWaiting() const { return _currently_waiting.load(); }
270 
274  struct ClientInfo
275  {
277  std::string name;
279  std::string host;
281  std::string user;
283  nlohmann::json data;
284  };
285 
289  ClientInfo getClientInfo() const;
290 
294  void outputMessage(const std::string & message) const;
295 
296 private:
304 
308  void setClientInfo(const ClientInfo & info);
309 
313  void clientPoke();
314 
318  bool isClientInitialized() const { return _client_initialized.load(); }
323 
327  void setCurrentlyWaiting(const bool value = true) { _currently_waiting.store(value); }
328 
332  bool isTerminateRequested() const { return _terminate_requested.load(); }
336  void setTerminateRequested(const bool value = true) { _terminate_requested.store(value); }
337 
341  bool isKillRequested() const { return _kill_requested.load(); }
345  void setKillRequested() { _kill_requested.store(true); }
346 
350  void outputClientTiming(const std::string & message,
351  const std::chrono::time_point<std::chrono::steady_clock> & start) const;
352 
358  std::string clientTimeoutErrorMessage(const Real timeout,
359  const std::string & timeout_param_name,
360  const std::optional<std::string> & suffix = {}) const;
361 
365  void stopServer();
366 
368  const unsigned int * const _port;
370  const FileName * const _file_socket;
375 
377  std::atomic<bool> _client_initialized = false;
379  std::atomic<bool> _currently_waiting = false;
381  std::atomic<bool> _terminate_requested = false;
383  std::atomic<bool> _kill_requested = false;
385  std::atomic<int64_t> _last_client_poke = 0;
386 
388  std::optional<ClientInfo> _client_info;
390  mutable std::mutex _client_info_lock;
391 
393  std::weak_ptr<HttpServer> _server_weak_ptr;
395  std::unique_ptr<std::thread> _server_thread_ptr;
396 
398  std::vector<std::unique_ptr<ControlledValueBase>> _controlled_values;
401 };
402 
403 template <class value_T, class key_T>
404 value_T
405 WebServerControl::convertJSON(const nlohmann::json & json_value, const key_T & key)
406 {
407  try
408  {
409  return json_value[key].template get<value_T>();
410  }
411  catch (const std::exception & e)
412  {
413  std::ostringstream message;
414  message << "While parsing '" << key << "' " << e.what();
415  throw std::runtime_error(message.str());
416  }
417 }
std::optional< nlohmann::json > _json
The JSON data, if any.
void setTerminateRequested(const bool value=true)
Set for the control to terminate the solve; used by /terminate in the server.
bool isTerminateRequested() const
Whether or not the client has called /terminate.
void setError(const std::string &error)
Set the error message.
std::atomic< bool > _currently_waiting
Whether or not the Control is currently waiting.
void addServerAction(const std::string &path, std::function< Response(const Request &, WebServerControl &)> &&action, const ServerActionOptions &options={})
Adds an action for the server to perform at the given path.
void setClientInfo(const ClientInfo &info)
Set the ClientInfo object received from the client during /initialize.
const std::set< std::string > getRequiredJSONKeys() const
bool isClientInitialized() const
Whether or not the client has called /initialize.
void setRequireWaiting(const bool value, const Moose::PassKey< WebServerControl >)
Set the require waiting flag; only accessible by the WebServerControl.
void addServerActionsInternal()
Adds the internal actions to the server.
MPI_Info info
Starts a webserver that an external process can connect to in order to send JSON messages to control ...
ClientInfo getClientInfo() const
Get the information sent by the client on initialize.
ErrorResponse(const std::string &error, const unsigned int status_code=400)
virtual void addServerActions()
Entrypoint for controls derived from this one to add additional actions.
std::set< std::string > _required_json_keys
JSON keys that are required in the data.
const Real _initial_client_timeout
Time in seconds to allow the client to initially communicate before timing out.
void setKillRequested()
Set for the control to kill the solve; used by /kill in the server.
const InputParameters & parameters() const
Get the parameters of the object.
Definition: MooseBase.h:131
void setCurrentlyWaiting(const bool value=true)
Set that the control is currently waiting; used by the server.
std::optional< ClientInfo > _client_info
Client information received on /initialize by the server.
void setClientInitialized()
Set that the client has called /initialized; used by the server.
void requireJSONKeys(std::initializer_list< std::string > &&keys)
Append keys to be required in JSON in the request data.
std::atomic< bool > _kill_requested
Whether or not the client has called /kill.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
Represents an error response to the client from the server.
WebServerControl(const InputParameters &parameters)
bool _require_initialized
Whether or not to require initialization.
const FileName *const _file_socket
File socket to listen on, if any.
virtual void setControllableValue(WebServerControl &control) override final
Sets the controllable value given the name and type via the controllable interface in control...
void startServer(const Moose::PassKey< StartWebServerControlAction >)
Start the server.
void requireJSONKey(const std::string &key)
Append a key to be required in JSON in the request data.
std::mutex _client_info_lock
Lock for _client_info as it is written by the server thread.
bool isKillRequested() const
Get whether or not the client sent the kill command.
void outputMessage(const std::string &message) const
Output a message with the prefix of this control type and name.
std::optional< nlohmann::json > _json
The underlying JSON data, if any.
std::vector< std::unique_ptr< ControlledValueBase > > _controlled_values
The values received to control; filled on rank 0 from the server and then broadcast.
const nlohmann::json & getJSON() const
const unsigned int *const _port
Port to listen on, if any.
std::optional< std::string > _error
The error message, if any.
void setRequireInitialized(const bool value, const Moose::PassKey< WebServerControl >)
Set the require initialized flag; only accessible by the WebServerControl.
std::string name
Client name.
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
T _value
The underlying value.
std::atomic< int64_t > _last_client_poke
The most recent time we&#39;ve heard from the client.
Options to be passed to addServerAction.
const std::string & getError() const
virtual void execute() override final
Execute the control.
std::mutex _controlled_values_mutex
Mutex to prevent threaded writes to _controlled_values.
ControlledValue(const std::string &name, const std::string &type, const T &value)
void outputClientTiming(const std::string &message, const std::chrono::time_point< std::chrono::steady_clock > &start) const
Output a timing message with the prefix of this control.
void clientPoke()
Store a client&#39;s poke, which is a timing used to determine the client timeout.
std::unique_ptr< std::thread > _server_thread_ptr
The server thread.
bool _require_waiting
Whether or not to require waiting.
The base class for a value that is produced by this registry.
const Real _client_timeout
Time in seconds to allow the client to communicate after init before timing out.
std::weak_ptr< HttpServer > _server_weak_ptr
Weak pointer to the server; the server itself is owned by the server thread.
std::atomic< bool > _terminate_requested
Whether or not the solve should be terminated in the next execute() call.
std::string host
Client host.
nlohmann::json data
Raw data.
ControlledValue(const std::string &name, const std::string &type)
Class containing a value to be controlled.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::string user
Client user.
void setJSON(const nlohmann::json &json, const Moose::PassKey< WebServerControl >)
Set the JSON data in the request.
Base class for Control objects.
Definition: Control.h:34
T value_type
The underlying type of the value.
bool isCurrentlyWaiting() const
Get whether or not the control is currently waiting.
Stores the information sent by the client on initialize.
Represents a response to the client from the server.
Starts the web server(s) for the WebServerControl objects.
const nlohmann::json & getJSON() const
unsigned int _status_code
The status code.
Represents a request from the client.
static InputParameters validParams()
std::string clientTimeoutErrorMessage(const Real timeout, const std::string &timeout_param_name, const std::optional< std::string > &suffix={}) const
Helper for producing an error message about a client timeout.
unsigned int getStatusCode() const
static value_T convertJSON(const nlohmann::json &json_value, const key_T &key)
Helper for converting a value to JSON for the given key.
void stopServer()
Stop the server if it exists and is running.
std::atomic< bool > _client_initialized
Whether or not the client has called /initialize.
RequestMethod
Define the valid methods for a client request.