Line data Source code
1 :
2 : /* Copyright (c) 2016-2017, Human Brain Project
3 : * Stefan.Eilemann@epfl.ch
4 : * Raphael.Dumusc@epfl.ch
5 : */
6 :
7 : #ifndef ZEROEQ_HTTP_SERVER_H
8 : #define ZEROEQ_HTTP_SERVER_H
9 :
10 : #include <zeroeq/http/api.h>
11 : #include <zeroeq/http/request.h>
12 : #include <zeroeq/http/types.h>
13 :
14 : #include <zeroeq/log.h>
15 : #include <zeroeq/receiver.h> // base class
16 :
17 : namespace zeroeq
18 : {
19 : namespace http
20 : {
21 : /**
22 : * Serves HTTP GET and PUT requests for servus::Serializable objects.
23 : *
24 : * Behaves semantically like a Publisher (for GET) and Subscriber (for PUT),
25 : * except uses HTTP with JSON payload as the protocol. Requests are served
26 : * synchronously (as per HTTP spec). Objects are available under their
27 : * Serializable::getTypeName(), with '::' replaced by '/'. The REST API is case
28 : * insensitive. For example, zerobuf::render::Camera is served at
29 : * 'GET|PUT [uri]/zerobuf/render/camera'.
30 : *
31 : * Announces itself if a zeroconf implementation is available, including
32 : * "Type=ZeroEQ" in the zeroconf record.
33 : *
34 : * Not thread safe.
35 : *
36 : * Example: @include tests/http/server.cpp
37 : */
38 : class Server : public zeroeq::Receiver
39 : {
40 : public:
41 : /** @name Setup */
42 : //@{
43 : /**
44 : * Construct a new HTTP server.
45 : *
46 : * To process requests on the incoming port, call receive().
47 : *
48 : * If no hostname is given, the server listens on all interfaces
49 : * (INADDR_ANY). If no port is given, the server selects a random port. Use
50 : * getURI() to retrieve the chosen parameters.
51 : *
52 : * @param uri The server address in the form "[tcp://][hostname][:port]"
53 : * @param shared a shared receiver, see Receiver constructor.
54 : * @throw std::runtime_error on malformed URI or connection issues.
55 : */
56 : ZEROEQHTTP_API Server(const URI& uri, Receiver& shared);
57 : ZEROEQHTTP_API explicit Server(const URI& uri);
58 : ZEROEQHTTP_API explicit Server(Receiver& shared);
59 1 : explicit Server(Server& shared)
60 1 : : Server(static_cast<Receiver&>(shared))
61 : {
62 1 : }
63 : ZEROEQHTTP_API Server();
64 :
65 : /** Destruct this http server. */
66 : ZEROEQHTTP_API ~Server();
67 :
68 : /**
69 : * Create a new Server when requested.
70 : *
71 : * The creation and parameters depend on the following command line
72 : * parameters:
73 : * * --zeroeq-http-server [host][:port]: Enable the server. The optional
74 : * parameters configure the web server, running by default on INADDR_ANY
75 : * and a randomly chosen port
76 : */
77 : ZEROEQHTTP_API
78 : static std::unique_ptr<Server> parse(int argc, const char* const* argv);
79 : ZEROEQHTTP_API
80 : static std::unique_ptr<Server> parse(int argc, const char* const* argv,
81 : Receiver& shared);
82 : /**
83 : * Get the publisher URI.
84 : *
85 : * Contains the used hostname and port, if none where given in the
86 : * constructor uri.
87 : *
88 : * @return the publisher URI.
89 : */
90 : ZEROEQHTTP_API const URI& getURI() const;
91 :
92 : /**
93 : * Get the underlying socket descriptor.
94 : *
95 : * Can be used by client code to be notified when new data is available and
96 : * subsequently call receive.
97 : *
98 : * @return the socket descriptor.
99 : * @throw std::runtime_error if the descriptor could not be obtained.
100 : * @note not supported on Windows due to ZMQ limitations, will throw
101 : * std::runtime_error
102 : */
103 : ZEROEQHTTP_API SocketDescriptor getSocketDescriptor() const;
104 : //@}
105 :
106 : /**
107 : * Handle a single method on a given endpoint.
108 : *
109 : * @param method to handle
110 : * @param endpoint the endpoint to receive requests for during receive()
111 : * @param func the callback function for serving the request
112 : * @return true if subscription was successful, false otherwise
113 : * @sa Request
114 : */
115 : bool handle(Method method, const std::string& endpoint, RESTFunc func);
116 :
117 : /** @name Object registration for PUT and GET requests */
118 : //@{
119 : /**
120 : * Handle PUT and GET for the given object.
121 : *
122 : * @param object the object to update and serve on receive()
123 : * @return true if subscription was successful, false otherwise
124 : */
125 7 : bool handle(servus::Serializable& object)
126 : {
127 7 : return handlePUT(object) && handleGET(object);
128 : }
129 :
130 : /**
131 : * @overload
132 : * @param object the object to update and serve on receive()
133 : * @param endpoint use this as the URL endpoint instead of the default
134 : * servus::Serializable::getTypeName()
135 : */
136 : ZEROEQHTTP_API bool handle(const std::string& endpoint,
137 : servus::Serializable& object);
138 :
139 : /** Remove PUT and GET handling for given object. */
140 : ZEROEQHTTP_API bool remove(const servus::Serializable& object);
141 :
142 : /** Remove all handling for given endpoint. */
143 : ZEROEQHTTP_API bool remove(const std::string& endpoint);
144 :
145 : /**
146 : * Subscribe a serializable object to receive updates from HTTP PUT
147 : * requests.
148 : *
149 : * Every update will be directly applied on the object during receive()
150 : * using fromJSON(). To track updates on the object, the serializable's
151 : * updated function is called accordingly.
152 : *
153 : * The subscribed object instance has to be valid until remove().
154 : *
155 : * @param object the object to update on receive()
156 : * @return true if subscription was successful, false otherwise
157 : */
158 : ZEROEQHTTP_API bool handlePUT(servus::Serializable& object);
159 :
160 : /**
161 : * @overload
162 : * @param object the object to update on receive()
163 : * @param endpoint use this as the URL endpoint instead of the default
164 : * servus::Serializable::getTypeName()
165 : */
166 : ZEROEQHTTP_API bool handlePUT(const std::string& endpoint,
167 : servus::Serializable& object);
168 :
169 : /**
170 : * Subscribe an endpoint to receive HTTP PUT requests.
171 : *
172 : * Every receival of the endpoint will call the registered callback
173 : * function.
174 : *
175 : * @param endpoint the endpoint to receive PUT requests for during receive()
176 : * @param func the callback function for serving the PUT request
177 : * @return true if subscription was successful, false otherwise
178 : */
179 : ZEROEQHTTP_API
180 : bool handlePUT(const std::string& endpoint, const PUTFunc& func);
181 :
182 : /**
183 : * @overload
184 : * @param endpoint the endpoint to receive PUT requests for during receive()
185 : * @param schema describes data layout of endpoint
186 : * @param func the callback function for serving the PUT request
187 : */
188 : ZEROEQHTTP_API bool handlePUT(const std::string& endpoint,
189 : const std::string& schema,
190 : const PUTFunc& func);
191 :
192 : /**
193 : * Subscribe an endpoint to receive HTTP PUT requests with payload.
194 : *
195 : * Every receival of the endpoint will call the registered callback
196 : * function.
197 : *
198 : * @param endpoint the endpoint to receive PUT requests for during receive()
199 : * @param func the callback function for serving the PUT request
200 : * @return true if subscription was successful, false otherwise
201 : */
202 : ZEROEQHTTP_API bool handlePUT(const std::string& endpoint,
203 : const PUTPayloadFunc& func);
204 :
205 : /**
206 : * @overload
207 : * @param endpoint the endpoint to receive PUT requests for during receive()
208 : * @param schema describes data layout of the endpoint
209 : * @param func the callback function for serving the PUT request
210 : */
211 : ZEROEQHTTP_API bool handlePUT(const std::string& endpoint,
212 : const std::string& schema,
213 : const PUTPayloadFunc& func);
214 : /**
215 : * Subscribe a serializable object to serve HTTP GET requests.
216 : *
217 : * Every request will be directly handled during receive() by using
218 : * toJSON(). To track updates on the object, the serializable's received
219 : * function is called accordingly.
220 : *
221 : * The subscribed object instance has to be valid until remove().
222 : *
223 : * @param object the object to serve during receive()
224 : * @return true if subscription was successful, false otherwise
225 : */
226 : ZEROEQHTTP_API bool handleGET(const servus::Serializable& object);
227 :
228 : /**
229 : * @overload
230 : * @param object the object to serve during receive()
231 : * @param endpoint use this as the URL endpoint instead of the default
232 : * servus::Serializable::getTypeName()
233 : */
234 : ZEROEQHTTP_API bool handleGET(const std::string& endpoint,
235 : const servus::Serializable& object);
236 :
237 : /**
238 : * Subscribe an endpoint to serve HTTP GET requests.
239 : *
240 : * Every request will be directly handled during receive() by calling the
241 : * registered GET function.
242 : *
243 : * @param endpoint the endpoint to serve during receive()
244 : * @param func the callback function for serving the GET request
245 : * @return true if subscription was successful, false otherwise
246 : */
247 : ZEROEQHTTP_API
248 : bool handleGET(const std::string& endpoint, const GETFunc& func);
249 :
250 : /**
251 : * @overload
252 : * @param endpoint the endpoint to serve during receive()
253 : * @param schema describes data layout of the endpoint
254 : * @param func the callback function for serving the GET request
255 : */
256 : ZEROEQHTTP_API
257 : bool handleGET(const std::string& endpoint, const std::string& schema,
258 : const GETFunc& func);
259 :
260 : /**
261 : * @return the registered schema for the given object, or empty if not
262 : * registered.
263 : */
264 : ZEROEQHTTP_API
265 : std::string getSchema(const servus::Serializable& object) const;
266 :
267 : /** @overload */
268 : ZEROEQHTTP_API std::string getSchema(const std::string& endpoint) const;
269 : //@}
270 :
271 : protected:
272 : /**
273 : * Respond to a request.
274 : *
275 : * This function can be overridden in derived classes to implement special
276 : * processing such as filtering for certain sources or http methods.
277 : * @param request to process.
278 : * @return future response to a request.
279 : */
280 : virtual std::future<Response> respondTo(Request& request) const;
281 :
282 : private:
283 : class Impl;
284 : std::unique_ptr<Impl> _impl;
285 :
286 : // Receiver API
287 : void addSockets(std::vector<detail::Socket>& entries) final;
288 : bool process(detail::Socket& socket) final;
289 : };
290 : }
291 : }
292 :
293 : #endif
|