xref: /aosp_15_r20/external/ot-br-posix/src/web/web-service/web_server.cpp (revision 4a64e381480ef79f0532b2421e44e6ee336b8e0d)
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 #define OTBR_LOG_TAG "WEB"
35 
36 #include "web/web-service/web_server.hpp"
37 
38 #define BOOST_NO_CXX11_SCOPED_ENUMS
39 #include <boost/filesystem.hpp>
40 #undef BOOST_NO_CXX11_SCOPED_ENUMS
41 
42 #include <server_http.hpp>
43 
44 #include "common/code_utils.hpp"
45 #include "common/logging.hpp"
46 
47 #define OT_ADD_PREFIX_PATH "^/add_prefix"
48 #define OT_AVAILABLE_NETWORK_PATH "^/available_network$"
49 #define OT_DELETE_PREFIX_PATH "^/delete_prefix"
50 #define OT_FORM_NETWORK_PATH "^/form_network$"
51 #define OT_GET_NETWORK_PATH "^/get_properties$"
52 #define OT_JOIN_NETWORK_PATH "^/join_network$"
53 #define OT_GET_QRCODE_PATH "^/get_qrcode$"
54 #define OT_SET_NETWORK_PATH "^/settings$"
55 #define OT_COMMISSIONER_START_PATH "^/commission$"
56 #define OT_REQUEST_METHOD_GET "GET"
57 #define OT_REQUEST_METHOD_POST "POST"
58 #define OT_RESPONSE_SUCCESS_STATUS "HTTP/1.1 200 OK\r\n"
59 #define OT_RESPONSE_HEADER_LENGTH "Content-Length: "
60 #define OT_RESPONSE_HEADER_CSS_TYPE "\r\nContent-Type: text/css"
61 #define OT_RESPONSE_HEADER_TEXT_HTML_TYPE "\r\nContent-Type: text/html; charset=utf-8"
62 #define OT_RESPONSE_HEADER_TYPE "Content-Type: application/json\r\n charset=utf-8"
63 #define OT_RESPONSE_PLACEHOLD "\r\n\r\n"
64 #define OT_RESPONSE_FAILURE_STATUS "HTTP/1.1 400 Bad Request\r\n"
65 #define OT_BUFFER_SIZE 1024
66 
67 namespace otbr {
68 namespace Web {
69 
EscapeHtml(std::string & content)70 static void EscapeHtml(std::string &content)
71 {
72     std::string output;
73 
74     output.reserve(content.size());
75     for (char c : content)
76     {
77         switch (c)
78         {
79         case '&':
80             output.append("&amp;");
81             break;
82         case '<':
83             output.append("&lt;");
84             break;
85         case '>':
86             output.append("&gt;");
87             break;
88         case '"':
89             output.append("&quot;");
90             break;
91         case '\'':
92             output.append("&apos;");
93             break;
94         default:
95             output.push_back(c);
96             break;
97         }
98     }
99 
100     output.swap(content);
101 }
102 
WebServer(void)103 WebServer::WebServer(void)
104     : mServer(new HttpServer())
105 {
106 }
107 
~WebServer(void)108 WebServer::~WebServer(void)
109 {
110     delete mServer;
111 }
112 
Init()113 void WebServer::Init()
114 {
115     std::string networkName, extPanId;
116 
117     if (mWpanService.GetWpanServiceStatus(networkName, extPanId) > 0)
118     {
119         return;
120     }
121 }
122 
StartWebServer(const char * aIfName,const char * aListenAddr,uint16_t aPort)123 void WebServer::StartWebServer(const char *aIfName, const char *aListenAddr, uint16_t aPort)
124 {
125     if (aListenAddr != nullptr)
126     {
127         mServer->config.address = aListenAddr;
128     }
129     mServer->config.port = aPort;
130     mWpanService.SetInterfaceName(aIfName);
131     Init();
132     ResponseGetQRCode();
133     ResponseJoinNetwork();
134     ResponseFormNetwork();
135     ResponseAddOnMeshPrefix();
136     ResponseDeleteOnMeshPrefix();
137     ResponseGetStatus();
138     ResponseGetAvailableNetwork();
139     ResponseCommission();
140     DefaultHttpResponse();
141 
142     try
143     {
144         mServer->start();
145     } catch (const std::exception &e)
146     {
147         otbrLogCrit("failed to start web server: %s", e.what());
148         abort();
149     }
150 }
151 
StopWebServer(void)152 void WebServer::StopWebServer(void)
153 {
154     try
155     {
156         mServer->stop();
157     } catch (const std::exception &e)
158     {
159         otbrLogCrit("failed to stop web server: %s", e.what());
160     }
161 }
162 
HandleHttpRequest(const char * aUrl,const char * aMethod,HttpRequestCallback aCallback)163 void WebServer::HandleHttpRequest(const char *aUrl, const char *aMethod, HttpRequestCallback aCallback)
164 {
165     mServer->resource[aUrl][aMethod] = [aCallback, this](std::shared_ptr<HttpServer::Response> response,
166                                                          std::shared_ptr<HttpServer::Request>  request) {
167         try
168         {
169             std::string httpResponse;
170             if (aCallback != nullptr)
171             {
172                 httpResponse = aCallback(request->content.string(), this);
173             }
174 
175             *response << OT_RESPONSE_SUCCESS_STATUS << OT_RESPONSE_HEADER_LENGTH << httpResponse.length()
176                       << OT_RESPONSE_PLACEHOLD << httpResponse;
177         } catch (std::exception &e)
178         {
179             std::string content = e.what();
180             EscapeHtml(content);
181             *response << OT_RESPONSE_FAILURE_STATUS << OT_RESPONSE_HEADER_LENGTH << strlen(e.what())
182                       << OT_RESPONSE_PLACEHOLD << content;
183         }
184     };
185 }
186 
DefaultResourceSend(const HttpServer & aServer,const std::shared_ptr<HttpServer::Response> & aResponse,const std::shared_ptr<std::ifstream> & aIfStream)187 void DefaultResourceSend(const HttpServer                            &aServer,
188                          const std::shared_ptr<HttpServer::Response> &aResponse,
189                          const std::shared_ptr<std::ifstream>        &aIfStream)
190 {
191     static std::vector<char> buffer(OT_BUFFER_SIZE); // Safe when server is running on one thread
192 
193     std::streamsize readLength;
194 
195     if ((readLength = aIfStream->read(&buffer[0], buffer.size()).gcount()) > 0)
196     {
197         aResponse->write(&buffer[0], readLength);
198         if (readLength == static_cast<std::streamsize>(buffer.size()))
199         {
200             aResponse->send([&aServer, aResponse, aIfStream](const boost::system::error_code &ec) {
201                 if (!ec)
202                 {
203                     DefaultResourceSend(aServer, aResponse, aIfStream);
204                 }
205                 else
206                 {
207                     std::cerr << "Connection interrupted" << std::endl;
208                 }
209             });
210         }
211     }
212 }
213 
DefaultHttpResponse(void)214 void WebServer::DefaultHttpResponse(void)
215 {
216     mServer->default_resource[OT_REQUEST_METHOD_GET] = [this](std::shared_ptr<HttpServer::Response> response,
217                                                               std::shared_ptr<HttpServer::Request>  request) {
218         try
219         {
220             auto webRootPath = boost::filesystem::canonical(WEB_FILE_PATH);
221             auto path        = boost::filesystem::canonical(webRootPath / request->path);
222 
223             // Check if path is within webRootPath
224             if (std::distance(webRootPath.begin(), webRootPath.end()) > std::distance(path.begin(), path.end()) ||
225                 !std::equal(webRootPath.begin(), webRootPath.end(), path.begin()))
226             {
227                 throw std::invalid_argument("path must be within root path");
228             }
229             if (boost::filesystem::is_directory(path))
230             {
231                 path /= "index.html";
232             }
233             if (!(boost::filesystem::exists(path) && boost::filesystem::is_regular_file(path)))
234             {
235                 throw std::invalid_argument("file does not exist");
236             }
237 
238             std::string cacheControl, etag;
239 
240             auto ifs = std::make_shared<std::ifstream>();
241             ifs->open(path.string(), std::ifstream::in | std::ios::binary | std::ios::ate);
242             std::string extension = path.extension().string();
243             std::string header    = "";
244             if (extension == ".css")
245             {
246                 header = OT_RESPONSE_HEADER_CSS_TYPE;
247             }
248             else if (extension == ".html")
249             {
250                 header = OT_RESPONSE_HEADER_TEXT_HTML_TYPE;
251             }
252 
253             if (*ifs)
254             {
255                 auto length = ifs->tellg();
256                 ifs->seekg(0, std::ios::beg);
257 
258                 *response << OT_RESPONSE_SUCCESS_STATUS << cacheControl << etag << OT_RESPONSE_HEADER_LENGTH << length
259                           << header << OT_RESPONSE_PLACEHOLD;
260 
261                 DefaultResourceSend(*mServer, response, ifs);
262             }
263             else
264             {
265                 throw std::invalid_argument("could not read file");
266             }
267 
268         } catch (const std::exception &e)
269         {
270             std::string content = "Could not open path `" + request->path + "`: " + e.what();
271             EscapeHtml(content);
272             *response << OT_RESPONSE_FAILURE_STATUS << OT_RESPONSE_HEADER_LENGTH << content.length()
273                       << OT_RESPONSE_PLACEHOLD << content;
274         }
275     };
276 }
277 
HandleJoinNetworkRequest(const std::string & aJoinRequest,void * aUserData)278 std::string WebServer::HandleJoinNetworkRequest(const std::string &aJoinRequest, void *aUserData)
279 {
280     WebServer *webServer = static_cast<WebServer *>(aUserData);
281 
282     return webServer->HandleJoinNetworkRequest(aJoinRequest);
283 }
284 
HandleGetQRCodeRequest(const std::string & aGetQRCodeRequest,void * aUserData)285 std::string WebServer::HandleGetQRCodeRequest(const std::string &aGetQRCodeRequest, void *aUserData)
286 {
287     WebServer *webServer = static_cast<WebServer *>(aUserData);
288 
289     return webServer->HandleGetQRCodeRequest(aGetQRCodeRequest);
290 }
291 
HandleFormNetworkRequest(const std::string & aFormRequest,void * aUserData)292 std::string WebServer::HandleFormNetworkRequest(const std::string &aFormRequest, void *aUserData)
293 {
294     WebServer *webServer = static_cast<WebServer *>(aUserData);
295 
296     return webServer->HandleFormNetworkRequest(aFormRequest);
297 }
298 
HandleAddPrefixRequest(const std::string & aAddPrefixRequest,void * aUserData)299 std::string WebServer::HandleAddPrefixRequest(const std::string &aAddPrefixRequest, void *aUserData)
300 {
301     WebServer *webServer = static_cast<WebServer *>(aUserData);
302 
303     return webServer->HandleAddPrefixRequest(aAddPrefixRequest);
304 }
305 
HandleDeletePrefixRequest(const std::string & aDeletePrefixRequest,void * aUserData)306 std::string WebServer::HandleDeletePrefixRequest(const std::string &aDeletePrefixRequest, void *aUserData)
307 {
308     WebServer *webServer = static_cast<WebServer *>(aUserData);
309 
310     return webServer->HandleDeletePrefixRequest(aDeletePrefixRequest);
311 }
312 
HandleGetStatusRequest(const std::string & aGetStatusRequest,void * aUserData)313 std::string WebServer::HandleGetStatusRequest(const std::string &aGetStatusRequest, void *aUserData)
314 {
315     WebServer *webServer = static_cast<WebServer *>(aUserData);
316 
317     return webServer->HandleGetStatusRequest(aGetStatusRequest);
318 }
319 
HandleGetAvailableNetworkResponse(const std::string & aGetAvailableNetworkRequest,void * aUserData)320 std::string WebServer::HandleGetAvailableNetworkResponse(const std::string &aGetAvailableNetworkRequest,
321                                                          void              *aUserData)
322 {
323     WebServer *webServer = static_cast<WebServer *>(aUserData);
324 
325     return webServer->HandleGetAvailableNetworkResponse(aGetAvailableNetworkRequest);
326 }
327 
HandleCommission(const std::string & aCommissionRequest,void * aUserData)328 std::string WebServer::HandleCommission(const std::string &aCommissionRequest, void *aUserData)
329 {
330     WebServer *webServer = static_cast<WebServer *>(aUserData);
331 
332     return webServer->HandleCommission(aCommissionRequest);
333 }
334 
ResponseJoinNetwork(void)335 void WebServer::ResponseJoinNetwork(void)
336 {
337     HandleHttpRequest(OT_JOIN_NETWORK_PATH, OT_REQUEST_METHOD_POST, HandleJoinNetworkRequest);
338 }
339 
ResponseGetQRCode(void)340 void WebServer::ResponseGetQRCode(void)
341 {
342     HandleHttpRequest(OT_GET_QRCODE_PATH, OT_REQUEST_METHOD_GET, HandleGetQRCodeRequest);
343 }
344 
ResponseFormNetwork(void)345 void WebServer::ResponseFormNetwork(void)
346 {
347     HandleHttpRequest(OT_FORM_NETWORK_PATH, OT_REQUEST_METHOD_POST, HandleFormNetworkRequest);
348 }
349 
ResponseAddOnMeshPrefix(void)350 void WebServer::ResponseAddOnMeshPrefix(void)
351 {
352     HandleHttpRequest(OT_ADD_PREFIX_PATH, OT_REQUEST_METHOD_POST, HandleAddPrefixRequest);
353 }
354 
ResponseDeleteOnMeshPrefix(void)355 void WebServer::ResponseDeleteOnMeshPrefix(void)
356 {
357     HandleHttpRequest(OT_DELETE_PREFIX_PATH, OT_REQUEST_METHOD_POST, HandleDeletePrefixRequest);
358 }
359 
ResponseGetStatus(void)360 void WebServer::ResponseGetStatus(void)
361 {
362     HandleHttpRequest(OT_GET_NETWORK_PATH, OT_REQUEST_METHOD_GET, HandleGetStatusRequest);
363 }
364 
ResponseGetAvailableNetwork(void)365 void WebServer::ResponseGetAvailableNetwork(void)
366 {
367     HandleHttpRequest(OT_AVAILABLE_NETWORK_PATH, OT_REQUEST_METHOD_GET, HandleGetAvailableNetworkResponse);
368 }
369 
ResponseCommission(void)370 void WebServer::ResponseCommission(void)
371 {
372     HandleHttpRequest(OT_COMMISSIONER_START_PATH, OT_REQUEST_METHOD_POST, HandleCommission);
373 }
374 
HandleJoinNetworkRequest(const std::string & aJoinRequest)375 std::string WebServer::HandleJoinNetworkRequest(const std::string &aJoinRequest)
376 {
377     return mWpanService.HandleJoinNetworkRequest(aJoinRequest);
378 }
379 
HandleGetQRCodeRequest(const std::string & aGetQRCodeRequest)380 std::string WebServer::HandleGetQRCodeRequest(const std::string &aGetQRCodeRequest)
381 {
382     OTBR_UNUSED_VARIABLE(aGetQRCodeRequest);
383     return mWpanService.HandleGetQRCodeRequest();
384 }
385 
HandleFormNetworkRequest(const std::string & aFormRequest)386 std::string WebServer::HandleFormNetworkRequest(const std::string &aFormRequest)
387 {
388     return mWpanService.HandleFormNetworkRequest(aFormRequest);
389 }
390 
HandleAddPrefixRequest(const std::string & aAddPrefixRequest)391 std::string WebServer::HandleAddPrefixRequest(const std::string &aAddPrefixRequest)
392 {
393     return mWpanService.HandleAddPrefixRequest(aAddPrefixRequest);
394 }
395 
HandleDeletePrefixRequest(const std::string & aDeletePrefixRequest)396 std::string WebServer::HandleDeletePrefixRequest(const std::string &aDeletePrefixRequest)
397 {
398     return mWpanService.HandleDeletePrefixRequest(aDeletePrefixRequest);
399 }
400 
HandleGetStatusRequest(const std::string & aGetStatusRequest)401 std::string WebServer::HandleGetStatusRequest(const std::string &aGetStatusRequest)
402 {
403     OTBR_UNUSED_VARIABLE(aGetStatusRequest);
404     return mWpanService.HandleStatusRequest();
405 }
406 
HandleGetAvailableNetworkResponse(const std::string & aGetAvailableNetworkRequest)407 std::string WebServer::HandleGetAvailableNetworkResponse(const std::string &aGetAvailableNetworkRequest)
408 {
409     OTBR_UNUSED_VARIABLE(aGetAvailableNetworkRequest);
410     return mWpanService.HandleAvailableNetworkRequest();
411 }
412 
HandleCommission(const std::string & aCommissionRequest)413 std::string WebServer::HandleCommission(const std::string &aCommissionRequest)
414 {
415     return mWpanService.HandleCommission(aCommissionRequest);
416 }
417 
418 } // namespace Web
419 } // namespace otbr
420