Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / third_party / ot-br-posix / repo / src / web / web-service / web_server.cpp
1 /*
2  *  Copyright (c) 2017, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 /**
30  * @file
31  *   This file implements the web server of border router
32  */
33
34 #include "web/web-service/web_server.hpp"
35
36 #define BOOST_NO_CXX11_SCOPED_ENUMS
37 #include <boost/filesystem.hpp>
38 #undef BOOST_NO_CXX11_SCOPED_ENUMS
39
40 #include <server_http.hpp>
41
42 #include "common/code_utils.hpp"
43
44 #define OT_ADD_PREFIX_PATH "^/add_prefix"
45 #define OT_AVAILABLE_NETWORK_PATH "^/available_network$"
46 #define OT_DELETE_PREFIX_PATH "^/delete_prefix"
47 #define OT_FORM_NETWORK_PATH "^/form_network$"
48 #define OT_GET_NETWORK_PATH "^/get_properties$"
49 #define OT_JOIN_NETWORK_PATH "^/join_network$"
50 #define OT_SET_NETWORK_PATH "^/settings$"
51 #define OT_COMMISSIONER_START_PATH "^/commission$"
52 #define OT_REQUEST_METHOD_GET "GET"
53 #define OT_REQUEST_METHOD_POST "POST"
54 #define OT_RESPONSE_SUCCESS_STATUS "HTTP/1.1 200 OK\r\n"
55 #define OT_RESPONSE_HEADER_LENGTH "Content-Length: "
56 #define OT_RESPONSE_HEADER_CSS_TYPE "\r\nContent-Type: text/css"
57 #define OT_RESPONSE_HEADER_TYPE "Content-Type: application/json\r\n charset=utf-8"
58 #define OT_RESPONSE_PLACEHOLD "\r\n\r\n"
59 #define OT_RESPONSE_FAILURE_STATUS "HTTP/1.1 400 Bad Request\r\n"
60 #define OT_BUFFER_SIZE 1024
61
62 namespace otbr {
63 namespace Web {
64
65 static void EscapeHtml(std::string &content)
66 {
67     std::string output;
68
69     output.reserve(content.size());
70     for (char c : content)
71     {
72         switch (c)
73         {
74         case '&':
75             output.append("&amp;");
76             break;
77         case '<':
78             output.append("&lt;");
79             break;
80         case '>':
81             output.append("&gt;");
82             break;
83         case '"':
84             output.append("&quot;");
85             break;
86         case '\'':
87             output.append("&apos;");
88             break;
89         default:
90             output.push_back(c);
91             break;
92         }
93     }
94
95     output.swap(content);
96 }
97
98 WebServer::WebServer(void)
99     : mServer(new HttpServer())
100 {
101 }
102
103 WebServer::~WebServer(void)
104 {
105     delete mServer;
106 }
107
108 void WebServer::Init()
109 {
110     std::string networkName, extPanId;
111
112     if (mWpanService.GetWpanServiceStatus(networkName, extPanId) > 0)
113     {
114         return;
115     }
116 }
117
118 void WebServer::StartWebServer(const char *aIfName, const char *aListenAddr, uint16_t aPort)
119 {
120     if (aListenAddr != nullptr)
121     {
122         mServer->config.address = aListenAddr;
123     }
124     mServer->config.port = aPort;
125     mWpanService.SetInterfaceName(aIfName);
126     Init();
127     ResponseJoinNetwork();
128     ResponseFormNetwork();
129     ResponseAddOnMeshPrefix();
130     ResponseDeleteOnMeshPrefix();
131     ResponseGetStatus();
132     ResponseGetAvailableNetwork();
133     ResponseCommission();
134     DefaultHttpResponse();
135     mServer->start();
136 }
137
138 void WebServer::StopWebServer(void)
139 {
140     mServer->stop();
141 }
142
143 void WebServer::HandleHttpRequest(const char *aUrl, const char *aMethod, HttpRequestCallback aCallback)
144 {
145     mServer->resource[aUrl][aMethod] = [aCallback, this](std::shared_ptr<HttpServer::Response> response,
146                                                          std::shared_ptr<HttpServer::Request>  request) {
147         try
148         {
149             std::string httpResponse;
150             if (aCallback != nullptr)
151             {
152                 httpResponse = aCallback(request->content.string(), this);
153             }
154
155             *response << OT_RESPONSE_SUCCESS_STATUS << OT_RESPONSE_HEADER_LENGTH << httpResponse.length()
156                       << OT_RESPONSE_PLACEHOLD << httpResponse;
157         } catch (std::exception &e)
158         {
159             std::string content = e.what();
160             EscapeHtml(content);
161             *response << OT_RESPONSE_FAILURE_STATUS << OT_RESPONSE_HEADER_LENGTH << strlen(e.what())
162                       << OT_RESPONSE_PLACEHOLD << content;
163         }
164     };
165 }
166
167 void DefaultResourceSend(const HttpServer &                           aServer,
168                          const std::shared_ptr<HttpServer::Response> &aResponse,
169                          const std::shared_ptr<std::ifstream> &       aIfStream)
170 {
171     static std::vector<char> buffer(OT_BUFFER_SIZE); // Safe when server is running on one thread
172
173     std::streamsize readLength;
174
175     if ((readLength = aIfStream->read(&buffer[0], buffer.size()).gcount()) > 0)
176     {
177         aResponse->write(&buffer[0], readLength);
178         if (readLength == static_cast<std::streamsize>(buffer.size()))
179         {
180             aServer.send(aResponse, [&aServer, aResponse, aIfStream](const boost::system::error_code &ec) {
181                 if (!ec)
182                 {
183                     DefaultResourceSend(aServer, aResponse, aIfStream);
184                 }
185                 else
186                 {
187                     std::cerr << "Connection interrupted" << std::endl;
188                 }
189             });
190         }
191     }
192 }
193
194 void WebServer::DefaultHttpResponse(void)
195 {
196     mServer->default_resource[OT_REQUEST_METHOD_GET] = [this](std::shared_ptr<HttpServer::Response> response,
197                                                               std::shared_ptr<HttpServer::Request>  request) {
198         try
199         {
200             auto webRootPath = boost::filesystem::canonical(WEB_FILE_PATH);
201             auto path        = boost::filesystem::canonical(webRootPath / request->path);
202             // Check if path is within webRootPath
203             if (std::distance(webRootPath.begin(), webRootPath.end()) > std::distance(path.begin(), path.end()) ||
204                 !std::equal(webRootPath.begin(), webRootPath.end(), path.begin()))
205             {
206                 throw std::invalid_argument("path must be within root path");
207             }
208             if (boost::filesystem::is_directory(path))
209             {
210                 path /= "index.html";
211             }
212             if (!(boost::filesystem::exists(path) && boost::filesystem::is_regular_file(path)))
213             {
214                 throw std::invalid_argument("file does not exist");
215             }
216
217             std::string cacheControl, etag;
218
219             auto ifs = std::make_shared<std::ifstream>();
220             ifs->open(path.string(), std::ifstream::in | std::ios::binary | std::ios::ate);
221             std::string extension = boost::filesystem::extension(path.string());
222             std::string style     = "";
223             if (extension == ".css")
224             {
225                 style = OT_RESPONSE_HEADER_CSS_TYPE;
226             }
227
228             if (*ifs)
229             {
230                 auto length = ifs->tellg();
231                 ifs->seekg(0, std::ios::beg);
232
233                 *response << OT_RESPONSE_SUCCESS_STATUS << cacheControl << etag << OT_RESPONSE_HEADER_LENGTH << length
234                           << style << OT_RESPONSE_PLACEHOLD;
235
236                 DefaultResourceSend(*mServer, response, ifs);
237             }
238             else
239             {
240                 throw std::invalid_argument("could not read file");
241             }
242
243         } catch (const std::exception &e)
244         {
245             std::string content = "Could not open path `" + request->path + "`: " + e.what();
246             EscapeHtml(content);
247             *response << OT_RESPONSE_FAILURE_STATUS << OT_RESPONSE_HEADER_LENGTH << content.length()
248                       << OT_RESPONSE_PLACEHOLD << content;
249         }
250     };
251 }
252
253 std::string WebServer::HandleJoinNetworkRequest(const std::string &aJoinRequest, void *aUserData)
254 {
255     WebServer *webServer = static_cast<WebServer *>(aUserData);
256
257     return webServer->HandleJoinNetworkRequest(aJoinRequest);
258 }
259
260 std::string WebServer::HandleFormNetworkRequest(const std::string &aFormRequest, void *aUserData)
261 {
262     WebServer *webServer = static_cast<WebServer *>(aUserData);
263
264     return webServer->HandleFormNetworkRequest(aFormRequest);
265 }
266
267 std::string WebServer::HandleAddPrefixRequest(const std::string &aAddPrefixRequest, void *aUserData)
268 {
269     WebServer *webServer = static_cast<WebServer *>(aUserData);
270
271     return webServer->HandleAddPrefixRequest(aAddPrefixRequest);
272 }
273
274 std::string WebServer::HandleDeletePrefixRequest(const std::string &aDeletePrefixRequest, void *aUserData)
275 {
276     WebServer *webServer = static_cast<WebServer *>(aUserData);
277
278     return webServer->HandleDeletePrefixRequest(aDeletePrefixRequest);
279 }
280
281 std::string WebServer::HandleGetStatusRequest(const std::string &aGetStatusRequest, void *aUserData)
282 {
283     WebServer *webServer = static_cast<WebServer *>(aUserData);
284
285     return webServer->HandleGetStatusRequest(aGetStatusRequest);
286 }
287
288 std::string WebServer::HandleGetAvailableNetworkResponse(const std::string &aGetAvailableNetworkRequest,
289                                                          void *             aUserData)
290 {
291     WebServer *webServer = static_cast<WebServer *>(aUserData);
292
293     return webServer->HandleGetAvailableNetworkResponse(aGetAvailableNetworkRequest);
294 }
295
296 std::string WebServer::HandleCommission(const std::string &aCommissionRequest, void *aUserData)
297 {
298     WebServer *webServer = static_cast<WebServer *>(aUserData);
299
300     return webServer->HandleCommission(aCommissionRequest);
301 }
302
303 void WebServer::ResponseJoinNetwork(void)
304 {
305     HandleHttpRequest(OT_JOIN_NETWORK_PATH, OT_REQUEST_METHOD_POST, HandleJoinNetworkRequest);
306 }
307
308 void WebServer::ResponseFormNetwork(void)
309 {
310     HandleHttpRequest(OT_FORM_NETWORK_PATH, OT_REQUEST_METHOD_POST, HandleFormNetworkRequest);
311 }
312
313 void WebServer::ResponseAddOnMeshPrefix(void)
314 {
315     HandleHttpRequest(OT_ADD_PREFIX_PATH, OT_REQUEST_METHOD_POST, HandleAddPrefixRequest);
316 }
317
318 void WebServer::ResponseDeleteOnMeshPrefix(void)
319 {
320     HandleHttpRequest(OT_DELETE_PREFIX_PATH, OT_REQUEST_METHOD_POST, HandleDeletePrefixRequest);
321 }
322
323 void WebServer::ResponseGetStatus(void)
324 {
325     HandleHttpRequest(OT_GET_NETWORK_PATH, OT_REQUEST_METHOD_GET, HandleGetStatusRequest);
326 }
327
328 void WebServer::ResponseGetAvailableNetwork(void)
329 {
330     HandleHttpRequest(OT_AVAILABLE_NETWORK_PATH, OT_REQUEST_METHOD_GET, HandleGetAvailableNetworkResponse);
331 }
332
333 void WebServer::ResponseCommission(void)
334 {
335     HandleHttpRequest(OT_COMMISSIONER_START_PATH, OT_REQUEST_METHOD_POST, HandleCommission);
336 }
337
338 std::string WebServer::HandleJoinNetworkRequest(const std::string &aJoinRequest)
339 {
340     return mWpanService.HandleJoinNetworkRequest(aJoinRequest);
341 }
342
343 std::string WebServer::HandleFormNetworkRequest(const std::string &aFormRequest)
344 {
345     return mWpanService.HandleFormNetworkRequest(aFormRequest);
346 }
347
348 std::string WebServer::HandleAddPrefixRequest(const std::string &aAddPrefixRequest)
349 {
350     return mWpanService.HandleAddPrefixRequest(aAddPrefixRequest);
351 }
352
353 std::string WebServer::HandleDeletePrefixRequest(const std::string &aDeletePrefixRequest)
354 {
355     return mWpanService.HandleDeletePrefixRequest(aDeletePrefixRequest);
356 }
357
358 std::string WebServer::HandleGetStatusRequest(const std::string &aGetStatusRequest)
359 {
360     OTBR_UNUSED_VARIABLE(aGetStatusRequest);
361     return mWpanService.HandleStatusRequest();
362 }
363
364 std::string WebServer::HandleGetAvailableNetworkResponse(const std::string &aGetAvailableNetworkRequest)
365 {
366     OTBR_UNUSED_VARIABLE(aGetAvailableNetworkRequest);
367     return mWpanService.HandleAvailableNetworkRequest();
368 }
369
370 std::string WebServer::HandleCommission(const std::string &aCommissionRequest)
371 {
372     return mWpanService.HandleCommission(aCommissionRequest);
373 }
374
375 } // namespace Web
376 } // namespace otbr