2 * Copyright (c) 2017, The OpenThread Authors.
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.
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.
31 * This file implements the web server of border router
34 #include "web/web-service/web_server.hpp"
36 #define BOOST_NO_CXX11_SCOPED_ENUMS
37 #include <boost/filesystem.hpp>
38 #undef BOOST_NO_CXX11_SCOPED_ENUMS
40 #include <server_http.hpp>
42 #include "common/code_utils.hpp"
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
65 static void EscapeHtml(std::string &content)
69 output.reserve(content.size());
70 for (char c : content)
75 output.append("&");
78 output.append("<");
81 output.append(">");
84 output.append(""");
87 output.append("'");
98 WebServer::WebServer(void)
99 : mServer(new HttpServer())
103 WebServer::~WebServer(void)
108 void WebServer::Init()
110 std::string networkName, extPanId;
112 if (mWpanService.GetWpanServiceStatus(networkName, extPanId) > 0)
118 void WebServer::StartWebServer(const char *aIfName, const char *aListenAddr, uint16_t aPort)
120 if (aListenAddr != nullptr)
122 mServer->config.address = aListenAddr;
124 mServer->config.port = aPort;
125 mWpanService.SetInterfaceName(aIfName);
127 ResponseJoinNetwork();
128 ResponseFormNetwork();
129 ResponseAddOnMeshPrefix();
130 ResponseDeleteOnMeshPrefix();
132 ResponseGetAvailableNetwork();
133 ResponseCommission();
134 DefaultHttpResponse();
138 void WebServer::StopWebServer(void)
143 void WebServer::HandleHttpRequest(const char *aUrl, const char *aMethod, HttpRequestCallback aCallback)
145 mServer->resource[aUrl][aMethod] = [aCallback, this](std::shared_ptr<HttpServer::Response> response,
146 std::shared_ptr<HttpServer::Request> request) {
149 std::string httpResponse;
150 if (aCallback != nullptr)
152 httpResponse = aCallback(request->content.string(), this);
155 *response << OT_RESPONSE_SUCCESS_STATUS << OT_RESPONSE_HEADER_LENGTH << httpResponse.length()
156 << OT_RESPONSE_PLACEHOLD << httpResponse;
157 } catch (std::exception &e)
159 std::string content = e.what();
161 *response << OT_RESPONSE_FAILURE_STATUS << OT_RESPONSE_HEADER_LENGTH << strlen(e.what())
162 << OT_RESPONSE_PLACEHOLD << content;
167 void DefaultResourceSend(const HttpServer & aServer,
168 const std::shared_ptr<HttpServer::Response> &aResponse,
169 const std::shared_ptr<std::ifstream> & aIfStream)
171 static std::vector<char> buffer(OT_BUFFER_SIZE); // Safe when server is running on one thread
173 std::streamsize readLength;
175 if ((readLength = aIfStream->read(&buffer[0], buffer.size()).gcount()) > 0)
177 aResponse->write(&buffer[0], readLength);
178 if (readLength == static_cast<std::streamsize>(buffer.size()))
180 aServer.send(aResponse, [&aServer, aResponse, aIfStream](const boost::system::error_code &ec) {
183 DefaultResourceSend(aServer, aResponse, aIfStream);
187 std::cerr << "Connection interrupted" << std::endl;
194 void WebServer::DefaultHttpResponse(void)
196 mServer->default_resource[OT_REQUEST_METHOD_GET] = [this](std::shared_ptr<HttpServer::Response> response,
197 std::shared_ptr<HttpServer::Request> request) {
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()))
206 throw std::invalid_argument("path must be within root path");
208 if (boost::filesystem::is_directory(path))
210 path /= "index.html";
212 if (!(boost::filesystem::exists(path) && boost::filesystem::is_regular_file(path)))
214 throw std::invalid_argument("file does not exist");
217 std::string cacheControl, etag;
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")
225 style = OT_RESPONSE_HEADER_CSS_TYPE;
230 auto length = ifs->tellg();
231 ifs->seekg(0, std::ios::beg);
233 *response << OT_RESPONSE_SUCCESS_STATUS << cacheControl << etag << OT_RESPONSE_HEADER_LENGTH << length
234 << style << OT_RESPONSE_PLACEHOLD;
236 DefaultResourceSend(*mServer, response, ifs);
240 throw std::invalid_argument("could not read file");
243 } catch (const std::exception &e)
245 std::string content = "Could not open path `" + request->path + "`: " + e.what();
247 *response << OT_RESPONSE_FAILURE_STATUS << OT_RESPONSE_HEADER_LENGTH << content.length()
248 << OT_RESPONSE_PLACEHOLD << content;
253 std::string WebServer::HandleJoinNetworkRequest(const std::string &aJoinRequest, void *aUserData)
255 WebServer *webServer = static_cast<WebServer *>(aUserData);
257 return webServer->HandleJoinNetworkRequest(aJoinRequest);
260 std::string WebServer::HandleFormNetworkRequest(const std::string &aFormRequest, void *aUserData)
262 WebServer *webServer = static_cast<WebServer *>(aUserData);
264 return webServer->HandleFormNetworkRequest(aFormRequest);
267 std::string WebServer::HandleAddPrefixRequest(const std::string &aAddPrefixRequest, void *aUserData)
269 WebServer *webServer = static_cast<WebServer *>(aUserData);
271 return webServer->HandleAddPrefixRequest(aAddPrefixRequest);
274 std::string WebServer::HandleDeletePrefixRequest(const std::string &aDeletePrefixRequest, void *aUserData)
276 WebServer *webServer = static_cast<WebServer *>(aUserData);
278 return webServer->HandleDeletePrefixRequest(aDeletePrefixRequest);
281 std::string WebServer::HandleGetStatusRequest(const std::string &aGetStatusRequest, void *aUserData)
283 WebServer *webServer = static_cast<WebServer *>(aUserData);
285 return webServer->HandleGetStatusRequest(aGetStatusRequest);
288 std::string WebServer::HandleGetAvailableNetworkResponse(const std::string &aGetAvailableNetworkRequest,
291 WebServer *webServer = static_cast<WebServer *>(aUserData);
293 return webServer->HandleGetAvailableNetworkResponse(aGetAvailableNetworkRequest);
296 std::string WebServer::HandleCommission(const std::string &aCommissionRequest, void *aUserData)
298 WebServer *webServer = static_cast<WebServer *>(aUserData);
300 return webServer->HandleCommission(aCommissionRequest);
303 void WebServer::ResponseJoinNetwork(void)
305 HandleHttpRequest(OT_JOIN_NETWORK_PATH, OT_REQUEST_METHOD_POST, HandleJoinNetworkRequest);
308 void WebServer::ResponseFormNetwork(void)
310 HandleHttpRequest(OT_FORM_NETWORK_PATH, OT_REQUEST_METHOD_POST, HandleFormNetworkRequest);
313 void WebServer::ResponseAddOnMeshPrefix(void)
315 HandleHttpRequest(OT_ADD_PREFIX_PATH, OT_REQUEST_METHOD_POST, HandleAddPrefixRequest);
318 void WebServer::ResponseDeleteOnMeshPrefix(void)
320 HandleHttpRequest(OT_DELETE_PREFIX_PATH, OT_REQUEST_METHOD_POST, HandleDeletePrefixRequest);
323 void WebServer::ResponseGetStatus(void)
325 HandleHttpRequest(OT_GET_NETWORK_PATH, OT_REQUEST_METHOD_GET, HandleGetStatusRequest);
328 void WebServer::ResponseGetAvailableNetwork(void)
330 HandleHttpRequest(OT_AVAILABLE_NETWORK_PATH, OT_REQUEST_METHOD_GET, HandleGetAvailableNetworkResponse);
333 void WebServer::ResponseCommission(void)
335 HandleHttpRequest(OT_COMMISSIONER_START_PATH, OT_REQUEST_METHOD_POST, HandleCommission);
338 std::string WebServer::HandleJoinNetworkRequest(const std::string &aJoinRequest)
340 return mWpanService.HandleJoinNetworkRequest(aJoinRequest);
343 std::string WebServer::HandleFormNetworkRequest(const std::string &aFormRequest)
345 return mWpanService.HandleFormNetworkRequest(aFormRequest);
348 std::string WebServer::HandleAddPrefixRequest(const std::string &aAddPrefixRequest)
350 return mWpanService.HandleAddPrefixRequest(aAddPrefixRequest);
353 std::string WebServer::HandleDeletePrefixRequest(const std::string &aDeletePrefixRequest)
355 return mWpanService.HandleDeletePrefixRequest(aDeletePrefixRequest);
358 std::string WebServer::HandleGetStatusRequest(const std::string &aGetStatusRequest)
360 OTBR_UNUSED_VARIABLE(aGetStatusRequest);
361 return mWpanService.HandleStatusRequest();
364 std::string WebServer::HandleGetAvailableNetworkResponse(const std::string &aGetAvailableNetworkRequest)
366 OTBR_UNUSED_VARIABLE(aGetAvailableNetworkRequest);
367 return mWpanService.HandleAvailableNetworkRequest();
370 std::string WebServer::HandleCommission(const std::string &aCommissionRequest)
372 return mWpanService.HandleCommission(aCommissionRequest);