diff --git a/.travis/check-docker b/.travis/check-docker index b2140b8faed..7cd830f82d8 100755 --- a/.travis/check-docker +++ b/.travis/check-docker @@ -72,11 +72,11 @@ main() readonly OTBR_DOCKER_PID=$(docker run --rm -dit \ --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1" \ - --privileged -p 8080:80 --dns=127.0.0.1 --volume "$DOCKER_PTY":/dev/ttyUSB0 otbr) + --privileged -p 8081:8081 --dns=127.0.0.1 --volume "$DOCKER_PTY":/dev/ttyUSB0 otbr) sleep 10 - sudo lsof -i :8080 + sudo lsof -i :8081 - local -r OTBR_WEB_URL="http://127.0.0.1:8080" + local -r OTBR_REST_URL="http://127.0.0.1:8081" local -r OT_MASTER_KEY=00112233445566778899aabbccddeeff local -r OT_XPANID=0011223344556677 local -r OT_PANID=0xface @@ -84,8 +84,7 @@ main() local -r OT_CHANNEL=12 local -r OT_NETWORK_NAME=OpenThreadDocker - curl "${OTBR_WEB_URL}"/index.html | grep 'What is OpenThread' - curl --header "Content-Type: application/json" --request POST --data "{\"masterKey\":\"${OT_MASTER_KEY}\",\"prefix\":\"fd11:22::\",\"defaultRoute\":true,\"extPanId\":\"${OT_XPANID}\",\"panId\":\"${OT_PANID}\",\"passphrase\":\"${OT_AGENT_PASSPHRASE}\",\"channel\":${OT_CHANNEL},\"networkName\":\"${OT_NETWORK_NAME}\"}" "${OTBR_WEB_URL}"/form_network | grep "success" + curl --header "Content-Type: application/json" --request POST --data "{\"masterKey\":\"${OT_MASTER_KEY}\",\"prefix\":\"fd11:22::\",\"defaultRoute\":true,\"extPanId\":\"${OT_XPANID}\",\"panId\":\"${OT_PANID}\",\"passphrase\":\"${OT_AGENT_PASSPHRASE}\",\"channel\":${OT_CHANNEL},\"networkName\":\"${OT_NETWORK_NAME}\"}" "${OTBR_REST_URL}"/v1/networks | grep "${OT_PANID}" } main "$@" diff --git a/CMakeLists.txt b/CMakeLists.txt index 48ffe9ec2c9..3b9cde48615 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,6 @@ option(OTBR_BACKBONE_ROUTER "Build Backbone Router" OFF) option(OTBR_DBUS "Build DBus support" OFF) option(OTBR_OPENWRT "Build OpenWrt support" OFF) option(OTBR_UNSECURE_JOIN "Enable unsecure joining" OFF) -option(OTBR_WEB "Build Web GUI" OFF) option(OTBR_REST "Build Rest Server" OFF) @@ -115,16 +114,6 @@ if(OTBR_REST) ) endif() -if(OTBR_WEB) - pkg_check_modules(JSONCPP jsoncpp REQUIRED) - set(Boost_USE_STATIC_LIBS ON) - set(Boost_USE_MULTITHREADED ON) - set(Boost_USE_STATIC_RUNTIME OFF) - find_package(Boost REQUIRED - COMPONENTS filesystem system) - set(OTBR_WEB_DATADIR ${CMAKE_INSTALL_FULL_DATADIR}/otbr-web) -endif() - if(OTBR_OPENWRT) target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_OPENWRT=1 @@ -147,7 +136,7 @@ if(SYSTEMD_FOUND) endif() -add_subdirectory(third_party EXCLUDE_FROM_ALL) +add_subdirectory(third_party) add_subdirectory(src) add_subdirectory(tools) diff --git a/etc/docker/docker_entrypoint.sh b/etc/docker/docker_entrypoint.sh index 1ac169db256..eb0c40ecf38 100755 --- a/etc/docker/docker_entrypoint.sh +++ b/etc/docker/docker_entrypoint.sh @@ -89,7 +89,6 @@ sed -i "s/^prefix.*$/prefix $NAT64_PREFIX/" /etc/tayga.conf sed -i "s/dns64.*$/dns64 $NAT64_PREFIX {};/" /etc/bind/named.conf.options echo "OTBR_AGENT_OPTS=\"-I $TUN_INTERFACE_NAME $BACKBONE_INTERFACE_ARG -d7 $RADIO_URL\"" >/etc/default/otbr-agent -echo "OTBR_WEB_OPTS=\"-I $TUN_INTERFACE_NAME -d7 -p 80\"" >/etc/default/otbr-web /app/script/server diff --git a/etc/rest/otbr-nginx.conf b/etc/rest/otbr-nginx.conf new file mode 100644 index 00000000000..9d922020c24 --- /dev/null +++ b/etc/rest/otbr-nginx.conf @@ -0,0 +1,47 @@ +# Copyright (c) 2020, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# Description: +# The Nginx configuration file for OTBR web. +# + +server { + listen 80; + listen [::]:80; + server_name _; + + location / { + root /var/otbr/frontend; + index index.html; + } + location /v1/ { + proxy_pass http://0.0.0.0:8081; + proxy_set_header Host $proxy_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + +} diff --git a/script/_nginx_config_install b/script/_nginx_config_install new file mode 100644 index 00000000000..14df1e114f8 --- /dev/null +++ b/script/_nginx_config_install @@ -0,0 +1,48 @@ +#!/bin/bash +# +# Copyright (c) 2020, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +readonly NGINX_CONF_DIR=/etc/nginx + +nginx_config_install() +{ + if command -v nginx; then + sudo cp etc/rest/otbr-nginx.conf "${NGINX_CONF_DIR}"/sites-available/ + sudo rm -rf "${NGINX_CONF_DIR}"/sites-enabled/default + sudo rm -rf "${NGINX_CONF_DIR}"/sites-enabled/otbr-nginx.conf + sudo ln -s "${NGINX_CONF_DIR}"/sites-available/otbr-nginx.conf "${NGINX_CONF_DIR}"/sites-enabled/ + + if have systemctl; then + sudo systemctl restart nginx + else + echo >&2 ' *** WARNING: systemctl not found. otbr cannot start on boot.' + fi + else + echo >&2 ' *** WARNING: nginx not found, please install nginx manually and run setup again' + fi +} diff --git a/script/_otbr b/script/_otbr index 772fd752539..c58e8c064b7 100644 --- a/script/_otbr +++ b/script/_otbr @@ -36,12 +36,12 @@ readonly BACKBONE_ROUTER="${BACKBONE_ROUTER:-0}" otbr_uninstall() { if have systemctl; then - sudo systemctl stop otbr-web otbr-agent || true - sudo systemctl disable otbr-web otbr-agent || true - ! sudo systemctl is-enabled otbr-web + sudo systemctl stop otbr-agent || true + sudo systemctl disable otbr-agent || true + ! sudo systemctl is-enabled ! sudo systemctl is-enabled otbr-agent fi - sudo killall otbr-web otbr-agent || true + sudo killall otbr-agent || true ( if cd "${OTBR_TOP_BUILDDIR}"; then @@ -66,7 +66,6 @@ otbr_install() "-DBUILD_TESTING=OFF" "-DCMAKE_INSTALL_PREFIX=/usr" "-DOTBR_DBUS=ON" - "-DOTBR_WEB=ON" "-DOTBR_REST=ON" "${otbr_options[@]}" ) @@ -91,9 +90,8 @@ otbr_install() if have systemctl; then sudo systemctl reload dbus sudo systemctl daemon-reload - sudo systemctl enable otbr-web otbr-agent || true + sudo systemctl enable otbr-agent || true sudo systemctl is-enabled otbr-agent || die 'Failed to enable otbr-agent!' - sudo systemctl is-enabled otbr-web || die 'Failed to enable otbr-web!' else echo >&2 ' *** WARNING: systemctl not found. otbr cannot start on boot.' fi diff --git a/script/bootstrap b/script/bootstrap index e6a6c63041a..c0eac636e67 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -84,8 +84,8 @@ install_packages_apt() fi } - # libjsoncpp - sudo apt-get install --no-install-recommends -y libjsoncpp1 libjsoncpp-dev + # nginx + sudo apt-get install --no-install-recommends -y nginx # reference device without REFERENCE_DEVICE || sudo apt-get install --no-install-recommends -y radvd @@ -115,11 +115,12 @@ install_packages_rpm() sudo $PM install -y tayga iptables sudo $PM install -y jsoncpp-devel sudo $PM install -y wget + sudo $PM install -y nginx } install_packages_brew() { - brew install boost cmake cpputest dbus jsoncpp ninja + brew install cmake cpputest dbus ninja nginx } install_packages_source() diff --git a/script/console b/script/console index 232e51aecd0..24b79731993 100755 --- a/script/console +++ b/script/console @@ -41,7 +41,7 @@ readonly RADIO_URL="${RADIO_URL:-spinel+hdlc+uart:///dev/ttyUSB0}" killall_services() { echo 'Closing services...' - sudo killall otbr-agent otbr-web || true + sudo killall otbr-agent || true } on_exit() @@ -54,7 +54,7 @@ main() { . "$BEFORE_HOOK" if have systemctl; then - sudo systemctl stop otbr-web otbr-agent || true + sudo systemctl stop otbr-agent || true fi killall_services @@ -62,7 +62,6 @@ main() ipforward_enable sudo sh -s < 0 || (received == -1 && (err == EAGAIN || err == EWOULDBLOCK)), error = OTBR_ERROR_REST); exit: + if (error != OTBR_ERROR_NONE) { if (received < 0) { - mResource->ErrorHandler(mResponse, HttpStatusCode::kStatusInternalServerError); + mResource->ErrorHandler(mResponse, HttpStatusCode::kStatusInternalServerError, "System call read error "); Write(); } else { - mResource->ErrorHandler(mResponse, HttpStatusCode::kStatusRequestTimeout); + mResource->ErrorHandler(mResponse, HttpStatusCode::kStatusRequestTimeout, + "Read timeout or sender stop sending"); Write(); } } @@ -250,7 +245,7 @@ void Connection::Handle(void) if (error != OTBR_ERROR_NONE) { - mResource->ErrorHandler(mResponse, HttpStatusCode::kStatusInternalServerError); + mResource->ErrorHandler(mResponse, HttpStatusCode::kStatusInternalServerError, "system call shutdown error"); Write(); } } @@ -269,7 +264,7 @@ void Connection::ProcessWaitCallback(void) { if (duration >= kCallbackTimeout) { - mResource->ErrorHandler(mResponse, HttpStatusCode::kStatusInternalServerError); + mResource->ErrorHandler(mResponse, HttpStatusCode::kStatusInternalServerError, "Callback handler timeout"); Write(); } } @@ -307,7 +302,6 @@ void Connection::Write(void) mWriteContent = mResponse.Serialize(); } - // Check we do have something to write. VerifyOrExit(mWriteContent.size() > 0, error = OTBR_ERROR_REST); sendLength = write(mFd, mWriteContent.c_str(), mWriteContent.size()); @@ -328,7 +322,6 @@ void Connection::Write(void) { if (errno == EINTR) { - // Try again Write(); } else @@ -350,5 +343,5 @@ bool Connection::IsComplete() const return mState == ConnectionState::kComplete; } -} // namespace rest +} // namespace Rest } // namespace otbr diff --git a/src/rest/connection.hpp b/src/rest/connection.hpp index 249f9d52ce3..2df2a162080 100644 --- a/src/rest/connection.hpp +++ b/src/rest/connection.hpp @@ -40,10 +40,8 @@ #include "rest/parser.hpp" #include "rest/resource.hpp" -using std::chrono::steady_clock; - namespace otbr { -namespace rest { +namespace Rest { /** * This class implements a Connection class of each socket connection. @@ -61,7 +59,7 @@ class Connection * @param[in] aFd The file descriptor for the conneciton. * */ - Connection(steady_clock::time_point aStartTime, Resource *aResource, int aFd); + Connection(std::chrono::steady_clock::time_point aStartTime, Resource *aResource, int aFd); /** * This method initializes the connection. @@ -108,7 +106,7 @@ class Connection void Disconnect(void); // Timestamp used for each check point of a connection - steady_clock::time_point mTimeStamp; + std::chrono::steady_clock::time_point mTimeStamp; // File descriptor for this connection int mFd; @@ -132,7 +130,7 @@ class Connection std::string mWriteContent; }; -} // namespace rest +} // namespace Rest } // namespace otbr #endif // OTBR_REST_CONNECTION_HPP_ diff --git a/src/web/web-service/frontend/index.html b/src/rest/frontend/index.html similarity index 96% rename from src/web/web-service/frontend/index.html rename to src/rest/frontend/index.html index 88291dbbdff..7d6bb6553e8 100644 --- a/src/web/web-service/frontend/index.html +++ b/src/rest/frontend/index.html @@ -49,7 +49,7 @@ - + Join diff --git a/src/web/web-service/frontend/res/css/styles.css b/src/rest/frontend/res/css/styles.css similarity index 99% rename from src/web/web-service/frontend/res/css/styles.css rename to src/rest/frontend/res/css/styles.css index b1765e678a1..ed91e3b0182 100644 --- a/src/web/web-service/frontend/res/css/styles.css +++ b/src/rest/frontend/res/css/styles.css @@ -85,7 +85,6 @@ text { background-color: #a3a2a2; } - .content { font-size: 13px; padding: 0 10px; diff --git a/src/web/web-service/frontend/res/img/android-desktop.png b/src/rest/frontend/res/img/android-desktop.png similarity index 100% rename from src/web/web-service/frontend/res/img/android-desktop.png rename to src/rest/frontend/res/img/android-desktop.png diff --git a/src/web/web-service/frontend/res/img/borderrouter.png b/src/rest/frontend/res/img/borderrouter.png similarity index 100% rename from src/web/web-service/frontend/res/img/borderrouter.png rename to src/rest/frontend/res/img/borderrouter.png diff --git a/src/web/web-service/frontend/res/img/favicon.png b/src/rest/frontend/res/img/favicon.png similarity index 100% rename from src/web/web-service/frontend/res/img/favicon.png rename to src/rest/frontend/res/img/favicon.png diff --git a/src/web/web-service/frontend/res/img/icon-info.png b/src/rest/frontend/res/img/icon-info.png similarity index 100% rename from src/web/web-service/frontend/res/img/icon-info.png rename to src/rest/frontend/res/img/icon-info.png diff --git a/src/web/web-service/frontend/res/img/ios-desktop.png b/src/rest/frontend/res/img/ios-desktop.png similarity index 100% rename from src/web/web-service/frontend/res/img/ios-desktop.png rename to src/rest/frontend/res/img/ios-desktop.png diff --git a/src/web/web-service/frontend/res/img/openthread_logo.png b/src/rest/frontend/res/img/openthread_logo.png similarity index 100% rename from src/web/web-service/frontend/res/img/openthread_logo.png rename to src/rest/frontend/res/img/openthread_logo.png diff --git a/src/web/web-service/frontend/res/js/app.js b/src/rest/frontend/res/js/app.js similarity index 61% rename from src/web/web-service/frontend/res/js/app.js rename to src/rest/frontend/res/js/app.js index 72e65bd1c96..3875af069cf 100644 --- a/src/web/web-service/frontend/res/js/app.js +++ b/src/rest/frontend/res/js/app.js @@ -89,6 +89,17 @@ ]; + $scope.RoleInt2String = + { + 0 : 'Disabled', + 1 : 'Detached', + 2 : 'Child' , + 3 : 'Router', + 4 : 'Leader' + + } + + $scope.thread = { networkName: 'OpenThreadDemo', extPanId: '1111111122222222', @@ -110,18 +121,63 @@ $scope.isLoading = false; - $scope.showScanAlert = function(ev) { + $scope.showScanAlert = function(ev,response) { + var alertMessage; + if (response.status == 200) + { + alertMessage = 'No available thread network now' + } + else + { + if ('ErrorDescription' in response.data) + { + alertMessage = response.data.ErrorDescription; + } + else if ('ErorMessage' in response.data) + { + alertMessage= response.data.ErorMessage; + } + else + { + alertMessage = 'Undefined response'; + } + } + $mdDialog.show( $mdDialog.alert() .parent(angular.element(document.querySelector('#popupContainer'))) .clickOutsideToClose(true) .title('Information') - .textContent('There is no available Thread network currently, please \ - wait a moment and retry it.') + .textContent(alertMessage) .ariaLabel('Alert Dialog Demo') .ok('Okay') ); }; + $scope.showStatusAlert = function(ev,response) { + var alertMessage; + if ('ErrorDescription' in response.data) + { + alertMessage = response.data.ErrorDescription; + } + else if ('ErrorMessage' in response.data) + { + alertMessage= response.data.ErrorMessage ; + } + else + { + alertMessage = 'Undefined response'; + } + $mdDialog.show( + $mdDialog.alert() + .parent(angular.element(document.querySelector('#popupContainer'))) + .clickOutsideToClose(true) + .title('Information') + .textContent(alertMessage) + .ariaLabel('Alert Dialog Demo') + .ok('Okay') + ); + }; + $scope.showPanels = function(index) { $scope.headerTitle = $scope.menu[index].title; for (var i = 0; i < 7; i++) { @@ -130,34 +186,64 @@ $scope.menu[index].show = true; if (index == 1) { $scope.isLoading = true; - $http.get('/available_network').then(function(response) { - $scope.isLoading = false; - if (response.data.error == 0) { - $scope.networksInfo = response.data.result; - } else { - $scope.showScanAlert(event); + $http.get('/v1/networks').then( + function successCallback(response) + { + $scope.isLoading = false; + if (response.status == 200 && response.data.length > 0) + { + $scope.networksInfo = response.data; + } + else + { + $scope.showScanAlert(event,response); + } + }, + function errorCallback(response) + { + $scope.isLoading = false; + $scope.showScanAlert(event,response); + } - }); + ); } + if (index == 3) { - $http.get('/get_properties').then(function(response) { - console.log(response); - if (response.data.error == 0) { - var statusJson = response.data.result; - $scope.status = []; - for (var i = 0; i < Object.keys(statusJson).length; i++) { - $scope.status.push({ - name: Object.keys(statusJson)[i], - value: statusJson[Object.keys(statusJson)[i]], - icon: 'res/img/icon-info.png', - }); + $http.get('/v1/node').then( + function successCallback(response) + { + if (response.status == 200){ + var NodeInfo = response.data; + $scope.status = []; + for (var i = 0; i < Object.keys(NodeInfo).length; i++) { + if (Object.keys(NodeInfo)[i] == 'Network:NodeType' || Object.keys(NodeInfo)[i] == 'NCP:State' ) + { + NodeInfo[Object.keys(NodeInfo)[i]] = $scope.RoleInt2String[NodeInfo[Object.keys(NodeInfo)[i]]]; + } + $scope.status.push + ({ + name: Object.keys(NodeInfo)[i], + value: NodeInfo[Object.keys(NodeInfo)[i]], + icon: 'res/img/icon-info.png', + }); + } + } + else { + $scope.showStatusAlert(event,response); + } + }, + function errorCallback(response){ + + $scope.showStatusAlert(event,response); + } - }); + + ); } if (index == 6) { $scope.dataInit(); - $scope.showTopology(); + $scope.showTopology(event); } }; @@ -166,6 +252,7 @@ sharedProperties.setNetworkInfo(item); $scope.index = index; $mdDialog.show({ + controller: DialogController, templateUrl: 'join.dialog.html', parent: angular.element(document.body), @@ -176,7 +263,9 @@ }; function DialogController($scope, $mdDialog, $http, $interval, sharedProperties) { + var index = sharedProperties.getIndex(); + var networkInfo = sharedProperties.getNetworkInfo(); $scope.isDisplay = false; $scope.thread = { masterKey: '00112233445566778899aabbccddeeff', @@ -184,13 +273,35 @@ defaultRoute: true, }; - $scope.showAlert = function(ev, result) { + $scope.showAlert = function(ev, response) { + + var alertMessage; + if (response.status == 200) + { + alertMessage = 'Successful' + } + else + { + if ('ErrorDescription' in response.data) + { + alertMessage = response.data.ErrorDescription; + } + else if ('ErorMessage' in response.data) + { + alertMessage= response.data.ErorMessage; + } + else + { + alertMessage = 'Failed bacause of Undefined response'; + } + } + $mdDialog.show( $mdDialog.alert() .parent(angular.element(document.querySelector('#popupContainer'))) .clickOutsideToClose(true) .title('Information') - .textContent('Join operation is ' + result) + .textContent('Join operation is ' + alertMessage) .ariaLabel('Alert Dialog Demo') .ok('Okay') .targetEvent(ev) @@ -212,21 +323,30 @@ prefix: $scope.thread.prefix, defaultRoute: $scope.thread.defaultRoute, index: index, + extPanId : networkInfo.ExtPanId, + networkName : networkInfo.NetworkName, + channel: networkInfo.Channel, + panId : '0x' + networkInfo.PanId.toString(16) }; var httpRequest = $http({ - method: 'POST', - url: '/join_network', + method: 'PUT', + url: '/v1/networks/current', data: data, }); - - httpRequest.then(function successCallback(response) { - $scope.res = response.data.result; - if (response.data.result == 'successful') { - $mdDialog.hide(); + httpRequest.then( + function successCallback(response) { + + if (response.status == 200) { + $mdDialog.hide(); + } + $scope.isDisplay = false; + $scope.showAlert(event, response); + }, + function errorCallback(response){ + $scope.isDisplay = false; + $scope.showAlert(event, response); } - $scope.isDisplay = false; - $scope.showAlert(event, response.data.result); - }); + ); }; $scope.cancel = function() { @@ -265,30 +385,59 @@ $scope.isForming = true; var httpRequest = $http({ method: 'POST', - url: '/form_network', + url: '/v1/networks', data: data, }); - httpRequest.then(function successCallback(response) { - $scope.res = response.data.result; - if (response.data.result == 'successful') { - $mdDialog.hide(); + httpRequest.then( + function successCallback(response) { + $scope.res = response.data.result; + if (response.status == 200) { + $mdDialog.hide(); + } + $scope.isForming = false; + $scope.showAlert(event, 'FORM', response); + }, + function errorCallback(response){ + $scope.isForming = false; + $scope.showAlert(event, 'FORM', response); } - $scope.isForming = false; - $scope.showAlert(event, 'FORM', response.data.result); - }); + + + ); }, function() { $mdDialog.cancel(); }); }; - $scope.showAlert = function(ev, operation, result) { + $scope.showAlert = function(ev, operation, response) { + var alertMessage; + if (response.status == 200) + { + alertMessage = 'Successful'; + } + else + { + if ('ErrorDescription' in response.data) + { + alertMessage = response.data.ErrorDescription; + } + else if ('ErorMessage' in response.data) + { + alertMessage= response.data.ErorMessage; + } + else + { + alertMessage = 'Failed bacause of Undefined response'; + } + } + $mdDialog.show( $mdDialog.alert() .parent(angular.element(document.querySelector('#popupContainer'))) .clickOutsideToClose(true) .title('Information') - .textContent(operation + ' operation is ' + result) + .textContent(operation + ' operation is ' + alertMessage) .ariaLabel('Alert Dialog Demo') .ok('Okay') .targetEvent(ev) @@ -313,12 +462,18 @@ }; var httpRequest = $http({ method: 'POST', - url: '/add_prefix', + url: '/v1/networks/current/prefix', data: data, }); httpRequest.then(function successCallback(response) { - $scope.showAlert(event, 'Add', response.data.result); + + $scope.showAlert(event, 'Add', response); + } + , function errorCallback(response){ + + $scope.showAlert(event, 'Add', response); + }); }, function() { $mdDialog.cancel(); @@ -338,13 +493,16 @@ prefix: $scope.setting.prefix, }; var httpRequest = $http({ - method: 'POST', - url: '/delete_prefix', + method: 'DELETE', + url: '/v1/networks/current/prefix', data: data, }); httpRequest.then(function successCallback(response) { - $scope.showAlert(event, 'Delete', response.data.result); + $scope.showAlert(event, 'Delete', response); + }, function errorCallback(response){ + $scope.showAlert(event, 'Delete', response); + }); }, function() { $mdDialog.cancel(); @@ -358,25 +516,22 @@ }; var httpRequest = $http({ method: 'POST', - url: '/commission', + url: '/v1/networks/current/commission', data: data, }); ev.target.disabled = true; httpRequest.then(function successCallback(response) { - if (response.data.error == 0) { - $scope.showAlert(event, 'Commission', 'success'); - } else { - $scope.showAlert(event, 'Commission', 'failed'); - } + $scope.showAlert(event, 'Commission', response); ev.target.disabled = false; + + }, function errorCallback(response){ + $scope.showAlert(event, 'Commission', response); + }); }; - $scope.restServerPort = '8081'; - $scope.ipAddr = window.location.hostname + ':' + $scope.restServerPort; - // Basic information line $scope.basicInfo = { 'NetworkName' : 'Unknown', @@ -389,7 +544,9 @@ $scope.nodeDetailInfo = 'Unknown'; // For response of Diagnostic $scope.networksDiagInfo = ''; - $scope.graphisReady = false; + $scope.topologyIsReady = false; + $scope.topologyIsLoading = false; + $scope.detailList = { 'ExtAddress': { 'title': false, 'content': true }, 'Rloc16': { 'title': false, 'content': true }, @@ -408,15 +565,23 @@ 'links': [] } - $scope.dataInit = function() { - - $http.get('http://' + $scope.ipAddr + '/node').then(function(response) { - - $scope.basicInfo = response.data; - console.log(response.data); + $scope.dataInit = function(ev) { + $http.get('/v1/node/network-name').then(function(response) + { + $scope.basicInfo.NetworkName = response.data; + }); + $http.get('/v1/node/rloc16').then(function(response) + { + $scope.basicInfo.Rloc16 = response.data; $scope.basicInfo.Rloc16 = $scope.intToHexString($scope.basicInfo.Rloc16,4); - $scope.basicInfo.LeaderData.LeaderRouterId = '0x' + $scope.intToHexString($scope.basicInfo.LeaderData.LeaderRouterId,2); }); + $http.get('/v1/node/leader-data').then(function(response) + { + $scope.basicInfo.LeaderData = response.data; + $scope.basicInfo.LeaderData.LeaderRouterId = '0x' + $scope.intToHexString($scope.basicInfo.LeaderData.LeaderRouterId<<10,4); + }); + + } $scope.isObject = function(obj) { return obj.constructor === Object; @@ -438,130 +603,185 @@ } return value; } - $scope.showTopology = function() { - var nodeMap = {} - var count, src, dist, rloc, child, rlocOfParent, rlocOfChild, diagOfNode, linkNode, childInfo; - $scope.graphisReady = false; + $scope.showTopologyAlert = function(ev,response) { + var alertMessage; + if (response.status == 200) + { + alertMessage = 'Empty diagnostic information returned, please check and try again later' + } + else + { + if ('ErrorDescription' in response.data) + { + alertMessage = response.data.ErrorDescription; + } + else if ('ErorMessage' in response.data) + { + alertMessage= response.data.ErorMessage; + } + else + { + alertMessage = 'Undefined response'; + } + } + + $mdDialog.show( + $mdDialog.alert() + .parent(angular.element(document.querySelector('#popupContainer'))) + .clickOutsideToClose(true) + .title('Information') + .textContent(alertMessage) + .ariaLabel('Alert Dialog Demo') + .ok('Okay') + ); + }; + + $scope.showTopology = function(event) { + + // Is loading + $scope.topologyIsLoading = true; + $scope.topologyIsReady = false; $scope.graphInfo = { 'nodes': [], 'links': [] }; - $http.get('http://' + $scope.ipAddr + '/diagnostics').then(function(response) { + $http.get('/v1/diagnostics').then(function successCallback(response) { + if (response.status == 200 && response.data.length > 0) + { + $scope.topology(response); + // Loads successfully + $scope.topologyIsLoading = false; + $scope.topologyIsReady = true; + } + else{ + // Not show anything + $scope.topologyIsReady = false; + $scope.topologyIsLoading = false; + $scope.showTopologyAlert(event,response); + } + }, + function errorCallback(response){ + $scope.showTopologyAlert(event,response); + // Not show anything + $scope.topologyIsReady = false; + $scope.topologyIsLoading = false; + } + + ) + } + + $scope.topology = function(response){ + var nodeMap = {}; + var count, src, dist, rloc, child, rlocOfParent, rlocOfChild, diagOfNode, linkNode, childInfo; + $scope.networksDiagInfo = response.data; - $scope.networksDiagInfo = response.data; - for (diagOfNode of $scope.networksDiagInfo){ + for (diagOfNode of $scope.networksDiagInfo){ - diagOfNode['RouteId'] = '0x' + $scope.intToHexString(diagOfNode['Rloc16'] >> 10,2); + diagOfNode['RouteId'] = '0x' + $scope.intToHexString(diagOfNode['Rloc16'] >> 10,2); - diagOfNode['Rloc16'] = '0x' + $scope.intToHexString(diagOfNode['Rloc16'],4); + diagOfNode['Rloc16'] = '0x' + $scope.intToHexString(diagOfNode['Rloc16'],4); - diagOfNode['LeaderData']['LeaderRouterId'] = '0x' + $scope.intToHexString(diagOfNode['LeaderData']['LeaderRouterId'],2); - for (linkNode of diagOfNode['Route']['RouteData']){ - linkNode['RouteId'] = '0x' + $scope.intToHexString(linkNode['RouteId'],2); - } + diagOfNode['LeaderData']['LeaderRouterId'] = '0x' + $scope.intToHexString(diagOfNode['LeaderData']['LeaderRouterId'],2); + + for (linkNode of diagOfNode['Route']['RouteData']){ + linkNode['RouteId'] = '0x' + $scope.intToHexString(linkNode['RouteId'],2); } + + } - count = 0; + count = 0; - for (diagOfNode of $scope.networksDiagInfo) { - if ('ChildTable' in diagOfNode) { + for (diagOfNode of $scope.networksDiagInfo) { + if ('ChildTable' in diagOfNode) { - rloc = parseInt(diagOfNode['Rloc16'],16).toString(16); - nodeMap[rloc] = count; + rloc = parseInt(diagOfNode['Rloc16'],16).toString(16); + nodeMap[rloc] = count; - if ( diagOfNode['RouteId'] == diagOfNode['LeaderData']['LeaderRouterId']) { - diagOfNode['Role'] = 'Leader'; - } else { - diagOfNode['Role'] = 'Router'; - } - - $scope.graphInfo.nodes.push(diagOfNode); + if (diagOfNode['RouteId'] == diagOfNode['LeaderData']['LeaderRouterId']) { + diagOfNode['Role'] = 'Leader'; + } + else { + diagOfNode['Role'] = 'Router'; + } + + $scope.graphInfo.nodes.push(diagOfNode); - if (diagOfNode['Rloc16'] === $scope.basicInfo.rloc16) { - $scope.nodeDetailInfo = diagOfNode - } - count = count + 1; + if (diagOfNode['Rloc16'] === $scope.basicInfo.rloc16) { + $scope.nodeDetailInfo = diagOfNode } + count = count + 1; } - // Num of Router is based on the diagnostic information - $scope.NumOfRouter = count; + } + + $scope.NumOfRouter = count; - // Index for a second loop - src = 0; - // Construct links - for (diagOfNode of $scope.networksDiagInfo) { - if ('ChildTable' in diagOfNode) { - // Link bewtwen routers - for (linkNode of diagOfNode['Route']['RouteData']) { - rloc = ( parseInt(linkNode['RouteId'],16) << 10).toString(16); - if (rloc in nodeMap) { - dist = nodeMap[rloc]; - if (src < dist) { - $scope.graphInfo.links.push({ - 'source': src, - 'target': dist, - 'weight': 1, - 'type': 0, - 'linkInfo': { - 'inQuality': linkNode['LinkQualityIn'], - 'outQuality': linkNode['LinkQualityOut'] - } - }); - } + // Index for a second loop + src = 0; + + // Construct links + for (diagOfNode of $scope.networksDiagInfo) { + if ('ChildTable' in diagOfNode) { + // Link bewtwen routers + for (linkNode of diagOfNode['Route']['RouteData']) { + rloc = ( parseInt(linkNode['RouteId'],16) << 10).toString(16); + if (rloc in nodeMap) { + dist = nodeMap[rloc]; + if (src < dist) { + $scope.graphInfo.links.push({ + 'source': src, + 'target': dist, + 'linkInfo': { + 'inQuality': linkNode['LinkQualityIn'], + 'outQuality': linkNode['LinkQualityOut'] + } + }); } } + } - // Link between router and child - for (childInfo of diagOfNode['ChildTable']) { - child = {}; - rlocOfParent = parseInt(diagOfNode['Rloc16'],16).toString(16); - rlocOfChild = (parseInt(diagOfNode['Rloc16'],16) + childInfo['ChildId']).toString(16); + // Link between router and child + for (childInfo of diagOfNode['ChildTable']) { + child = {}; + rlocOfParent = parseInt(diagOfNode['Rloc16'],16).toString(16); + rlocOfChild = (parseInt(diagOfNode['Rloc16'],16) + childInfo['ChildId']).toString(16); - src = nodeMap[rlocOfParent]; + src = nodeMap[rlocOfParent]; - child['Rloc16'] = '0x' + rlocOfChild; - child['RouteId'] = diagOfNode['RouteId']; - nodeMap[rlocOfChild] = count; - child['Role'] = 'Child'; - $scope.graphInfo.nodes.push(child); - $scope.graphInfo.links.push({ - 'source': src, - 'target': count, - 'weight': 1, - 'type': 1, - 'linkInfo': { - 'Timeout': childInfo['Timeout'], - 'Mode': childInfo['Mode'] - } - - }); - - count = count + 1; - } + child['Rloc16'] = '0x' + $scope.intToHexString(parseInt(diagOfNode['Rloc16'],16) + childInfo['ChildId'],4); + child['RouteId'] = diagOfNode['RouteId']; + nodeMap[rlocOfChild] = count; + child['Role'] = 'Child'; + $scope.graphInfo.nodes.push(child); + $scope.graphInfo.links.push({ + 'source': src, + 'target': count, + 'linkInfo': { + 'Timeout': childInfo['Timeout'], + 'Mode': childInfo['Mode'] + } + }); + count = count + 1; } - src = src + 1; } - - $scope.drawGraph(); - }) + src = src + 1; + } + $scope.drawGraph(); } - $scope.updateDetailLabel = function() { for (var detailInfoKey in $scope.detailList) { $scope.detailList[detailInfoKey]['title'] = false; } + for (var diagInfoKey in $scope.nodeDetailInfo) { if (diagInfoKey in $scope.detailList) { $scope.detailList[diagInfoKey]['title'] = true; } - } } - - + $scope.drawGraph = function() { var json, svg, tooltip, force; var scale, len; @@ -571,20 +791,21 @@ len = 125 * Math.sqrt(scale); // Topology graph - svg = d3.select('.d3graph').append('svg') - .attr('preserveAspectRatio', 'xMidYMid meet') - .attr('viewBox', '0, 0, ' + len.toString(10) + ', ' + (len / (3 / 2)).toString(10)); + svg = d3.select('.d3graph') + .append('svg') + .attr('preserveAspectRatio', 'xMidYMid meet') + .attr('viewBox', '0, 0, ' + len.toString(10) + ', ' + (len / (3 / 2)).toString(10)); // Legend svg.append('circle') .attr('cx',len-20) .attr('cy',10).attr('r', 3) - .style('fill', "#7e77f8") + .style('fill', '#7e77f8') .style('stroke', '#484e46') .style('stroke-width', '0.4px'); svg.append('circle') - .attr("cx",len-20) + .attr('cx',len-20) .attr('cy',20) .attr('r', 3) .style('fill', '#03e2dd') @@ -627,7 +848,6 @@ .text('Child') .style('font-size', '4px') .attr('alignment-baseline','middle'); - svg.append('text') .attr('x', len-45) .attr('y',10 ) @@ -635,7 +855,7 @@ .style('font-size', '4px') .attr('alignment-baseline','middle'); - // Tooltip style for each node + // Tooltip style for each node tooltip = d3.select('body') .append('div') .attr('class', 'tooltip') @@ -650,7 +870,7 @@ json = $scope.graphInfo; - + force .nodes(json.nodes) .links(json.links) @@ -673,11 +893,12 @@ return Math.sqrt(item.linkInfo.inQuality/2); else return Math.sqrt(0.5) }) - // Effect of mouseover on a line + // Tooltip effect of mouseover on a line .on('mouseover', function(item) { return tooltip.style('visibility', 'visible') .text(item.linkInfo); }) + .on('mousemove', function() { return tooltip.style('top', (d3.event.pageY - 10) + 'px') .style('left', (d3.event.pageX + 10) + 'px'); @@ -691,10 +912,10 @@ .data(json.nodes) .enter().append('g') .attr('class', function(item) { - return item.Role; + return item.Role; }) .call(force.drag) - // Tooltip effect of mouseover on a node + // Tooltip effect of mouseover on a node .on('mouseover', function(item) { return tooltip.style('visibility', 'visible') .text(item.Rloc16 ); @@ -734,11 +955,10 @@ .append('circle') .attr('r', '8') .attr('fill', '#7e77f8') + .style('stroke', '#484e46') .style('stroke-width', '1px') - .attr('class', function(item) { - return 'Stroke'; - }) + .attr('class', 'Stroke') // Effect that node will become bigger when mouseover .on('mouseover', function(item) { d3.select(this) @@ -753,8 +973,9 @@ }) .on('mouseout', function() { d3.select(this).transition().attr('r','8'); - return tooltip.style('visibility', 'hidden'); + return tooltip.style('visibility', 'hidden'); }) + // Effect that node will have a yellow edge when clicked .on('click', function(item) { d3.selectAll('.Stroke') @@ -768,6 +989,7 @@ $scope.updateDetailLabel(); }); }); + d3.selectAll('.Router') .append('circle') .attr('r', '8') @@ -816,8 +1038,9 @@ }); }); + $scope.updateDetailLabel(); - $scope.graphisReady = true; + } }; diff --git a/src/rest/json.cpp b/src/rest/json.cpp index 2e4450a1e8e..83aad84aa90 100644 --- a/src/rest/json.cpp +++ b/src/rest/json.cpp @@ -35,7 +35,7 @@ extern "C" { } namespace otbr { -namespace rest { +namespace Rest { namespace Json { static cJSON *Bytes2HexJson(const uint8_t *aBytes, uint8_t aLength) @@ -43,11 +43,37 @@ static cJSON *Bytes2HexJson(const uint8_t *aBytes, uint8_t aLength) char hex[2 * aLength + 1]; otbr::Utils::Bytes2Hex(aBytes, aLength, hex); - hex[2 * aLength] = '\0'; + for (auto &ch : hex) + { + ch = tolower(ch); + } return cJSON_CreateString(hex); } +static cJSON *Prefix2Json(const otIp6Prefix &aPrefix) +{ + std::string serilizedPrefix; + uint16_t twoBytes; + char prefix[5]; + + for (size_t i = 0; i < 8; ++i) + { + if (i % 2 == 1) + { + twoBytes = (aPrefix.mPrefix.mFields.m8[i - 1] << 8) + aPrefix.mPrefix.mFields.m8[i]; + sprintf(prefix, "%x", twoBytes); + if (i > 1) + { + serilizedPrefix += ":"; + } + serilizedPrefix += std::string(prefix); + } + } + + return cJSON_CreateString(serilizedPrefix.c_str()); +} + std::string String2JsonString(const std::string &aString) { std::string ret; @@ -95,6 +121,27 @@ static cJSON *CString2Json(const char *aString) return cJSON_CreateString(aString); } +static cJSON *ScanNetworks2Json(const std::vector &aResults) +{ + cJSON *networks = cJSON_CreateArray(); + for (const auto &result : aResults) + { + cJSON *network = cJSON_CreateObject(); + + cJSON_AddItemToObject(network, "IsJoinable", cJSON_CreateNumber(result.mIsJoinable)); + cJSON_AddItemToObject(network, "NetworkName", cJSON_CreateString(result.mNetworkName.c_str())); + cJSON_AddItemToObject(network, "ExtPanId", Bytes2HexJson(result.mExtendedPanId, OT_EXT_PAN_ID_SIZE)); + cJSON_AddItemToObject(network, "PanId", cJSON_CreateNumber(result.mPanId)); + cJSON_AddItemToObject(network, "MacAddress", Bytes2HexJson(result.mExtAddress, OT_EXT_ADDRESS_SIZE)); + cJSON_AddItemToObject(network, "Channel", cJSON_CreateNumber(result.mChannel)); + cJSON_AddItemToObject(network, "Rssi", cJSON_CreateNumber(result.mRssi)); + cJSON_AddItemToObject(network, "LQi", cJSON_CreateNumber(result.mLqi)); + + cJSON_AddItemToArray(networks, network); + } + + return networks; +} static cJSON *Mode2Json(const otLinkModeConfig &aMode) { cJSON *mode = cJSON_CreateObject(); @@ -106,19 +153,47 @@ static cJSON *Mode2Json(const otLinkModeConfig &aMode) return mode; } +static cJSON *Addr2Json(const uint8_t *aAddress, size_t aSize) +{ + std::string serilizedAddr; + uint16_t twoBytes; + char AddrField[5]; + + for (size_t i = 0; i < aSize; ++i) + { + if (i % 2 == 1) + { + twoBytes = (aAddress[i - 1] << 8) + aAddress[i]; + sprintf(AddrField, "%x", twoBytes); + if (i > 1) + { + serilizedAddr += ":"; + } + serilizedAddr += std::string(AddrField); + } + } + + return cJSON_CreateString(serilizedAddr.c_str()); +} + static cJSON *IpAddr2Json(const otIp6Address &aAddress) { std::string serilizedIpAddr; + uint16_t twoBytes; char ipAddrField[5]; - for (size_t i = 0; i < 8; ++i) + for (size_t i = 0; i < 16; ++i) { - sprintf(ipAddrField, "%x", aAddress.mFields.m16[i]); - if (i >= 1) + if (i % 2 == 1) { - serilizedIpAddr += ":"; + twoBytes = (aAddress.mFields.m8[i - 1] << 8) + aAddress.mFields.m8[i]; + sprintf(ipAddrField, "%x", twoBytes); + if (i > 1) + { + serilizedIpAddr += ":"; + } + serilizedIpAddr += std::string(ipAddrField); } - serilizedIpAddr += std::string(ipAddrField); } return cJSON_CreateString(serilizedIpAddr.c_str()); @@ -225,21 +300,65 @@ std::string IpAddr2JsonString(const otIp6Address &aAddress) return ret; } +std::string ScanNetworks2JsonString(const std::vector &aResults) +{ + std::string ret; + cJSON * networks = ScanNetworks2Json(aResults); + + ret = Json2String(networks); + cJSON_Delete(networks); + + return ret; +} + +std::string Network2JsonString(const otOperationalDataset &aDataset) +{ + cJSON * network = cJSON_CreateObject(); + std::string ret; + cJSON * value = nullptr; + + cJSON_AddItemToObject(network, "MeshLocalPrefix", + Addr2Json(aDataset.mMeshLocalPrefix.m8, OT_MESH_LOCAL_PREFIX_SIZE)); + cJSON_AddItemToObject(network, "MasterKey", Bytes2HexJson(aDataset.mMasterKey.m8, OT_MASTER_KEY_SIZE)); + cJSON_AddItemToObject(network, "PanId", cJSON_CreateNumber(aDataset.mPanId)); + cJSON_AddItemToObject(network, "ChannelMask", cJSON_CreateNumber(aDataset.mChannelMask)); + cJSON_AddItemToObject(network, "Channel", cJSON_CreateNumber(aDataset.mChannel)); + + value = cJSON_CreateArray(); + cJSON_AddItemToArray(value, cJSON_CreateNumber(aDataset.mSecurityPolicy.mRotationTime)); + cJSON_AddItemToArray(value, cJSON_CreateNumber(aDataset.mSecurityPolicy.mFlags)); + + cJSON_AddItemToObject(network, "SecurityPolicy", value); + cJSON_AddItemToObject(network, "PSKc", Bytes2HexJson(aDataset.mPskc.m8, OT_PSKC_MAX_SIZE)); + cJSON_AddItemToObject(network, "NetworkName", cJSON_CreateString(aDataset.mNetworkName.m8)); + cJSON_AddItemToObject(network, "ExtPanId", Bytes2HexJson(aDataset.mExtendedPanId.m8, OT_EXT_PAN_ID_SIZE)); + + ret = Json2String(network); + + cJSON_Delete(network); + + return ret; +} + std::string Node2JsonString(const NodeInfo &aNode) { cJSON * node = cJSON_CreateObject(); std::string ret; - cJSON_AddItemToObject(node, "State", cJSON_CreateNumber(aNode.mRole)); - cJSON_AddItemToObject(node, "NumOfRouter", cJSON_CreateNumber(aNode.mNumOfRouter)); - cJSON_AddItemToObject(node, "RlocAddress", IpAddr2Json(aNode.mRlocAddress)); - cJSON_AddItemToObject(node, "ExtAddress", Bytes2HexJson(aNode.mExtAddress, OT_EXT_ADDRESS_SIZE)); - cJSON_AddItemToObject(node, "NetworkName", cJSON_CreateString(aNode.mNetworkName.c_str())); - cJSON_AddItemToObject(node, "Rloc16", cJSON_CreateNumber(aNode.mRloc16)); - cJSON_AddItemToObject(node, "LeaderData", LeaderData2Json(aNode.mLeaderData)); - cJSON_AddItemToObject(node, "ExtPanId", Bytes2HexJson(aNode.mExtPanId, OT_EXT_PAN_ID_SIZE)); + cJSON_AddItemToObject(node, "WPAN service", cJSON_CreateString(aNode.mWpanService.c_str())); + cJSON_AddItemToObject(node, "NCP:State", cJSON_CreateNumber(aNode.mRole)); + cJSON_AddItemToObject(node, "NCP:Version", cJSON_CreateString(aNode.mVersion.c_str())); + cJSON_AddItemToObject(node, "NCP:HardwareAddress", Bytes2HexJson(aNode.mEui64.m8, OT_EXT_ADDRESS_SIZE)); + cJSON_AddItemToObject(node, "NCP:Channel", cJSON_CreateNumber(aNode.mChannel)); + cJSON_AddItemToObject(node, "Network:NodeType", cJSON_CreateNumber(aNode.mRole)); + cJSON_AddItemToObject(node, "Network:Name", cJSON_CreateString(aNode.mNetworkName.c_str())); + cJSON_AddItemToObject(node, "Network:XPANID", Bytes2HexJson(aNode.mExtPanId, OT_EXT_PAN_ID_SIZE)); + cJSON_AddItemToObject(node, "Network:PANID", cJSON_CreateNumber(aNode.mPanId)); + cJSON_AddItemToObject(node, "IPv6:MeshLocalPrefix", Addr2Json(aNode.mMeshLocalPrefix, OT_MESH_LOCAL_PREFIX_SIZE)); + cJSON_AddItemToObject(node, "IPv6:MeshLocalAddress", IpAddr2Json(aNode.mMeshLocalAddress)); ret = Json2String(node); + cJSON_Delete(node); return ret; @@ -402,6 +521,23 @@ std::string Mode2JsonString(const otLinkModeConfig &aMode) return ret; } +std::string PrefixList2JsonString(const std::vector &aConfig) +{ + std::string ret; + + cJSON *prefixList = cJSON_CreateArray(); + + for (size_t i = 0; i < aConfig.size(); i++) + { + cJSON_AddItemToArray(prefixList, Prefix2Json(aConfig[i].mPrefix)); + } + ret = Json2String(prefixList); + + cJSON_Delete(prefixList); + + return ret; +} + std::string Connectivity2JsonString(const otNetworkDiagConnectivity &aConnectivity) { cJSON * connectivity = Connectivity2Json(aConnectivity); @@ -472,13 +608,14 @@ std::string CString2JsonString(const char *aCString) return ret; } -std::string Error2JsonString(HttpStatusCode aErrorCode, std::string aErrorMessage) +std::string Error2JsonString(HttpStatusCode aErrorCode, std::string aErrorMessage, std::string aDescription) { std::string ret; cJSON * error = cJSON_CreateObject(); cJSON_AddItemToObject(error, "ErrorCode", cJSON_CreateNumber(static_cast(aErrorCode))); cJSON_AddItemToObject(error, "ErrorMessage", cJSON_CreateString(aErrorMessage.c_str())); + cJSON_AddItemToObject(error, "ErrorDescription", cJSON_CreateString(aDescription.c_str())); ret = Json2String(error); @@ -486,7 +623,126 @@ std::string Error2JsonString(HttpStatusCode aErrorCode, std::string aErrorMessag return ret; } +bool JsonString2NetworkConfiguration(std::string &aString, NetworkConfiguration &aNetwork) +{ + cJSON *value; + cJSON *jsonOut; + bool ret = true; + + jsonOut = cJSON_Parse(aString.c_str()); + value = cJSON_GetObjectItemCaseSensitive(jsonOut, "masterKey"); + VerifyOrExit(cJSON_IsString(value) && (value->valuestring != nullptr), ret = false); + aNetwork.mMasterKey = std::string(value->valuestring); + + value = cJSON_GetObjectItemCaseSensitive(jsonOut, "prefix"); + VerifyOrExit(cJSON_IsString(value) && (value->valuestring != nullptr), ret = false); + aNetwork.mPrefix = std::string(value->valuestring); + + value = cJSON_GetObjectItemCaseSensitive(jsonOut, "channel"); + VerifyOrExit(cJSON_IsNumber(value), ret = false); + aNetwork.mChannel = static_cast(value->valueint); + + value = cJSON_GetObjectItemCaseSensitive(jsonOut, "networkName"); + VerifyOrExit(cJSON_IsString(value) && (value->valuestring != nullptr), ret = false); + aNetwork.mNetworkName = std::string(value->valuestring); + + value = cJSON_GetObjectItemCaseSensitive(jsonOut, "passphrase"); + VerifyOrExit(cJSON_IsString(value) && (value->valuestring != nullptr), ret = false); + aNetwork.mPassphrase = std::string(value->valuestring); + + value = cJSON_GetObjectItemCaseSensitive(jsonOut, "panId"); + VerifyOrExit(cJSON_IsString(value) && (value->valuestring != nullptr), ret = false); + aNetwork.mPanId = std::string(value->valuestring); + + value = cJSON_GetObjectItemCaseSensitive(jsonOut, "extPanId"); + VerifyOrExit(cJSON_IsString(value) && (value->valuestring != nullptr), ret = false); + aNetwork.mExtPanId = std::string(value->valuestring); + + aNetwork.mDefaultRoute = false; + value = cJSON_GetObjectItemCaseSensitive(jsonOut, "defaultRoute"); + VerifyOrExit(cJSON_IsBool(value), ret = false); + if (cJSON_IsTrue(value)) + { + aNetwork.mDefaultRoute = true; + } + +exit: + + cJSON_Delete(jsonOut); + + return ret; +} + +bool JsonString2String(std::string aString, std::string aKey, std::string &aValue) +{ + cJSON *value; + cJSON *jsonOut; + bool ret = true; + + jsonOut = cJSON_Parse(aString.c_str()); + + value = cJSON_GetObjectItemCaseSensitive(jsonOut, aKey.c_str()); + if (cJSON_IsString(value) && (value->valuestring != nullptr)) + { + aValue = std::string(value->valuestring); + } + else + { + ret = false; + } + + cJSON_Delete(jsonOut); + + return ret; +} +bool JsonString2Bool(std::string aString, std::string aKey, bool &aValue) +{ + bool ret = true; + cJSON *value = nullptr; + cJSON *jsonOut = nullptr; + aValue = false; + jsonOut = cJSON_Parse(aString.c_str()); + value = cJSON_GetObjectItemCaseSensitive(jsonOut, aKey.c_str()); + VerifyOrExit(cJSON_IsBool(value), ret = false); + if (cJSON_IsTrue(value)) + { + aValue = true; + } + else + { + aValue = false; + } + +exit: + + cJSON_Delete(jsonOut); + + return ret; +} + +bool JsonString2Int(std::string aString, std::string aKey, int32_t &aValue) +{ + bool ret = true; + cJSON *value = nullptr; + cJSON *jsonOut = nullptr; + + jsonOut = cJSON_Parse(aString.c_str()); + value = cJSON_GetObjectItemCaseSensitive(jsonOut, aKey.c_str()); + + if (cJSON_IsNumber(value)) + { + aValue = static_cast(value->valueint); + } + else + { + ret = false; + } + + cJSON_Delete(jsonOut); + + return ret; +} } // namespace Json -} // namespace rest +} // namespace Rest } // namespace otbr diff --git a/src/rest/json.hpp b/src/rest/json.hpp index fbae5b1e8cf..f6a9eddf50e 100644 --- a/src/rest/json.hpp +++ b/src/rest/json.hpp @@ -28,182 +28,262 @@ /** * @file - * This file includes JSON formatter definition for RESTful HTTP server. + * This file includes JSON formater definition for RESTful HTTP server. */ #ifndef OTBR_REST_JSON_HPP_ #define OTBR_REST_JSON_HPP_ #include "openthread/link.h" +#include "openthread/netdata.h" #include "openthread/thread_ftd.h" #include "rest/types.hpp" #include "utils/hex.hpp" namespace otbr { -namespace rest { +namespace Rest { /** - * The functions within this namespace provides a tranformation from an object/string/number to a serialized Json - * string. + * The functions within this namespace provides a tranformation + * from an object/string/number to a serialized JSON string. * */ namespace Json { /** - * This method formats an integer to a Json number and serialize it to a string. + * This method formats an integer to a JSON number and serialize it to a string. * - * @param[in] aNumber an integer need to be format. + * @param[in] aNumber The given integer. * - * @returns a string serlialized by a Json number. + * @returns The serialized JSON string. * */ std::string Number2JsonString(const uint32_t &aNumber); /** - * This method formats a Bytes array to a Json string and serialize it to a string. + * This method formats a Bytes array to a JSON string and serialize it to a string. * - * @param[in] aBytes A Bytes array representing a hex number. + * @param[in] aBytes The given byte array. + * @param[in] aLength The length of the byte array. * - * @returns A string serlialized by a Json string. + * @returns The serialized JSON string. * */ std::string Bytes2HexJsonString(const uint8_t *aBytes, uint8_t aLength); /** - * This method formats a C string to a Json string and serialize it to a string. + * This method formats a C string to a JSON string and serialize it to a string. * - * @param[in] aCString A char pointer pointing to a C string. + * @param[in] aCString A char pointer to a C string. * - * @returns A string serlialized by a Json string. + * @returns The serialized JSON string. * */ std::string CString2JsonString(const char *aCString); /** - * This method formats a string to a Json string and serialize it to a string. + * This method formats a string to a JSON string and serialize it to a string. * - * @param[in] aString A string. + * @param[in] aString The given string. * - * @returns a string serlialized by a Json string. + * @returns The serialized JSON string. * */ std::string String2JsonString(const std::string &aString); /** - * This method formats a Node object to a Json object and serialize it to a string. + * This method formats a NodeInfo object to a JSON object and serialize it to a string. * - * @param[in] aNode A Node object. + * @param[in] aNode The given NodeInfo object. * - * @returns a string serlialized by a Json object. + * @returns The serialized JSON string. * */ std::string Node2JsonString(const NodeInfo &aNode); /** - * This method formats a vector including serveral Diagnostic object to a Json array and serialize it to a string. + * This method formats diagnostic informations to a JSON array and serialize it to a string. * - * @param[in] aDiagSet A vector including serveral Diagnostic object. + * @param[in] aDiagInfos The given diagnostic informations. * - * @returns A string serlialized by a Json array. + * @returns The serialized JSON string. * */ -std::string Diag2JsonString(const std::vector> &aDiagSet); +std::string Diag2JsonString(const std::vector> &aDiagInfos); /** - * This method formats an Ipv6Address to a Json string and serialize it to a string. + * This method formats an IPv6 address to a JSON string and serialize it to a string. * - * @param[in] aAddress An Ip6Address object. + * @param[in] aAddress An IPv6 address. * - * @returns A string serlialized by a Json string. + * @returns The serialized JSON string. * */ std::string IpAddr2JsonString(const otIp6Address &aAddress); /** - * This method formats a LinkModeConfig object to a Json object and serialize it to a string. + * This method formats extracts prefixes from a list of config object and format them as JSON Array. * - * @param[in] aMode A LinkModeConfig object. + * @param[in] aConfigs An array of Border Router Configurations. * - * @returns A string serlialized by a Json object. + * @returns The serialized JSON string. + * + */ +std::string PrefixList2JsonString(const std::vector &aConfigs); + +/** + * This method formats a LinkModeConfig object to a JSON object and serialize it to a string. + * + * @param[in] aMode A LinkModeConfig object. + * + * @returns The serialized JSON string. * */ std::string Mode2JsonString(const otLinkModeConfig &aMode); /** - * This method formats a Connectivity object to a Json object and serialize it to a string. + * This method formats a Connectivity object to a JSON object and serialize it to a string. * - * @param[in] aConnectivity A Connectivity object. + * @param[in] aConnectivity A Connectivity object. * - * @returns A string serlialized by a Json object. + * @returns The serialized JSON string. * */ std::string Connectivity2JsonString(const otNetworkDiagConnectivity &aConnectivity); /** - * This method formats a Route object to a Json object and serialize it to a string. + * This method formats a Route object to a JSON object and serialize it to a string. * - * @param[in] aRoute A Route object. + * @param[in] aRoute A Route object. * - * @returns A string serlialized by a Json object. + * @returns The serialized JSON string. * */ std::string Route2JsonString(const otNetworkDiagRoute &aRoute); /** - * This method formats a RouteData object to a Json object and serialize it to a string. + * This method formats a RouteData object to a JSON object and serialize it to a string. * - * @param[in] aRouteData A RouteData object. + * @param[in] aRouteData A RouteData object. * - * @returns A string serlialized by a Json object. + * @returns The serialized JSON string. * */ std::string RouteData2JsonString(const otNetworkDiagRouteData &aRouteData); /** - * This method formats a LeaderData object to a Json object and serialize it to a string. + * This method formats a LeaderData object to a JSON object and serialize it to a string. * - * @param[in] aLeaderData A LeaderData object. + * @param[in] aLeaderData A LeaderData object. * - * @returns A string serlialized by a Json object. + * @returns The serialized JSON string. * */ std::string LeaderData2JsonString(const otLeaderData &aLeaderData); /** - * This method formats a MacCounters object to a Json object and serialize it to a string. + * This method formats a MacCounters object to a JSON object and serialize it to a string. * - * @param[in] aMacCounters A MacCounters object. + * @param[in] aMacCounters A MacCounters object. * - * @returns A string serlialized by a Json object. + * @returns The serialized JSON string. * */ std::string MacCounters2JsonString(const otNetworkDiagMacCounters &aMacCounters); /** - * This method formats a ChildEntry object to a Json object and serialize it to a string. + * This method formats a ChildEntry object to a JSON object and serialize it to a string. * - * @param[in] aChildEntry A ChildEntry object. + * @param[in] aChildEntry A ChildEntry object. * - * @returns A string serlialized by a Json object. + * @returns The serialized JSON string. * */ std::string ChildTableEntry2JsonString(const otNetworkDiagChildEntry &aChildEntry); /** - * This method formats an error code and an error message to a Json object and serialize it to a string. + * This method formats an array of scan results to a JSON object and serialize it to a string. + * + * @param[in] aResults An array that contains scan results. + * + * @returns The serialized JSON string. + * + */ +std::string ScanNetworks2JsonString(const std::vector &aResults); + +/** + * This method formats an array of scan results to a JSON object and serialize it to a string. + * + * @param[in] aResults An array that contains scan results. + * + * @returns The serialized JSON string. + * + */ +std::string Network2JsonString(const otOperationalDataset &aDataset); + +/** + * This method formats an error code and an error message to a JSON object and serialize it to a string. + * + * @param[in] aErrorCode An enum HttpStatusCode such as '404'. + * @param[in] aErrorMessage An error message such as '404 Not Found'. + * @param[in] aDescription A detailed error information. + * + * @returns The serialized JSON string. + * + */ +std::string Error2JsonString(HttpStatusCode aErrorCode, std::string aErrorMessage, std::string aDescription); + +/** + * This method decodes a JSON object to a NetworkConfiguration object . + * + * @param[in] aString A string contains JSON object. + * @param[out] aNetwork A reference to a NetworkConfiguration instance that could be set within this method. + * + * @returns A bool value that indicates whether the string could be decoded to the NetworkConfiguration object + * + */ +bool JsonString2NetworkConfiguration(std::string &aString, NetworkConfiguration &aNetwork); + +/** + * This method decodes a JSON object to a NetworkConfiguration object . + * + * @param[in] aString A string contains JSON object. + * @param[in] aKey The key that may exist in the JSON object. + * @param[inout] aValue The string that is the value of the key in this JSON object. + * + * @returns A bool value that indicates whether the key exist in this JSON object. + * + */ +bool JsonString2String(std::string aString, std::string aKey, std::string &aValue); + +/** + * This method decodes a JSON object to a bool value . + * + * @param[in] aString A string contains JSON object. + * @param[in] aKey The key that may exist in the JSON object. + * @param[inout] aValue The bool value that is the value of the key in this JSON object. + * + * @returns A bool value that indicates whether the key exist in this JSON object. + * + */ +bool JsonString2Bool(std::string aString, std::string aKey, bool &aValue); + +/** + * This method decodes a JSON object to a bool value . * - * @param[in] aErrorCode An enum HttpStatusCode such as '404'. - * @param[in] aErrorMessage Error message such as '404 Not Found'. + * @param[in] aString A string contains JSON object. + * @param[in] aKey The key that may exist in the JSON object. + * @param[inout] aValue The integer value that is the value of the key in this JSON object. * - * @returns A string serlialized by a Json object. + * @returns A bool value that indicates whether the key exist in this JSON object. * */ -std::string Error2JsonString(HttpStatusCode aErrorCode, std::string aErrorMessage); +bool JsonString2Int(std::string aString, std::string aKey, int32_t &aValue); }; // namespace Json -} // namespace rest +} // namespace Rest } // namespace otbr #endif // OTBR_REST_JSON_HPP_ diff --git a/src/rest/parser.cpp b/src/rest/parser.cpp index 566f88cbb2a..2d682f1b737 100644 --- a/src/rest/parser.cpp +++ b/src/rest/parser.cpp @@ -30,9 +30,8 @@ #include #include - namespace otbr { -namespace rest { +namespace Rest { static int OnUrl(http_parser *parser, const char *at, size_t len) { @@ -110,5 +109,5 @@ void Parser::Process(const char *aBuf, size_t aLength) http_parser_execute(&mParser, &mSettings, aBuf, aLength); } -} // namespace rest +} // namespace Rest } // namespace otbr diff --git a/src/rest/parser.hpp b/src/rest/parser.hpp index bedf16b58a1..088d0368198 100644 --- a/src/rest/parser.hpp +++ b/src/rest/parser.hpp @@ -45,7 +45,7 @@ extern "C" { #include "rest/request.hpp" namespace otbr { -namespace rest { +namespace Rest { /** * This class implements Parser class in OTBR-REST which is used to parse the data from read buffer and form a request. @@ -63,7 +63,7 @@ class Parser Parser(Request *aRequest); /** - * This method initializea the http-parser. + * This method initializes the http-parser. * */ void Init(void); @@ -71,8 +71,8 @@ class Parser /** * This method performs a parse process. * - * @param[in] aBuf A pointer pointing to read buffer. - * @param[in] aLength An integer indicates how much data is to be processed by parser. + * @param[in] aBuf A pointer to read buffer. + * @param[in] aLength An integer indicates how much data is to be processed by parser. * */ void Process(const char *aBuf, size_t aLength); @@ -82,7 +82,7 @@ class Parser http_parser_settings mSettings; }; -} // namespace rest +} // namespace Rest } // namespace otbr #endif // OTBR_REST_PARSER_HPP_ diff --git a/src/rest/request.cpp b/src/rest/request.cpp index ac3dc300c34..01def57925e 100644 --- a/src/rest/request.cpp +++ b/src/rest/request.cpp @@ -29,7 +29,7 @@ #include "rest/request.hpp" namespace otbr { -namespace rest { +namespace Rest { Request::Request(void) : mComplete(false) @@ -102,5 +102,5 @@ bool Request::IsComplete(void) const return mComplete; } -} // namespace rest +} // namespace Rest } // namespace otbr diff --git a/src/rest/request.hpp b/src/rest/request.hpp index a8b7233357d..4575afcd6bd 100644 --- a/src/rest/request.hpp +++ b/src/rest/request.hpp @@ -41,7 +41,7 @@ #include "rest/types.hpp" namespace otbr { -namespace rest { +namespace Rest { /** * This class implements an instance to host services used by border router. @@ -105,7 +105,7 @@ class Request /** * This method returns the HTTP method of this request. * - * @returns A integer representing HTTP method. + * @returns The HTTP method. */ HttpMethod GetMethod() const; @@ -138,7 +138,7 @@ class Request bool mComplete; }; -} // namespace rest +} // namespace Rest } // namespace otbr #endif // OTBR_REST_REQUEST_HPP_ diff --git a/src/rest/resource.cpp b/src/rest/resource.cpp index 8d7519b76a9..75160f4f63f 100644 --- a/src/rest/resource.cpp +++ b/src/rest/resource.cpp @@ -28,32 +28,41 @@ #include "rest/resource.hpp" -#include "string.h" - -#define OT_PSKC_MAX_LENGTH 16 -#define OT_EXTENDED_PANID_LENGTH 8 - -#define OT_REST_RESOURCE_PATH_DIAGNOETIC "/diagnostics" -#define OT_REST_RESOURCE_PATH_NODE "/node" -#define OT_REST_RESOURCE_PATH_NODE_RLOC "/node/rloc" -#define OT_REST_RESOURCE_PATH_NODE_RLOC16 "/node/rloc16" -#define OT_REST_RESOURCE_PATH_NODE_EXTADDRESS "/node/ext-address" -#define OT_REST_RESOURCE_PATH_NODE_STATE "/node/state" -#define OT_REST_RESOURCE_PATH_NODE_NETWORKNAME "/node/network-name" -#define OT_REST_RESOURCE_PATH_NODE_LEADERDATA "/node/leader-data" -#define OT_REST_RESOURCE_PATH_NODE_NUMOFROUTER "/node/num-of-router" -#define OT_REST_RESOURCE_PATH_NODE_EXTPANID "/node/ext-panid" -#define OT_REST_RESOURCE_PATH_NETWORK "/networks" -#define OT_REST_RESOURCE_PATH_NETWORK_CURRENT "/networks/current" -#define OT_REST_RESOURCE_PATH_NETWORK_CURRENT_COMMISSION "/networks/commission" -#define OT_REST_RESOURCE_PATH_NETWORK_CURRENT_PREFIX "/networks/current/prefix" +#include + +#include "utils/pskc.hpp" + +#define OT_REST_RESOURCE_VERSION "/v1" + +#define OT_REST_RESOURCE_PATH_DIAGNOSTIC OT_REST_RESOURCE_VERSION "/diagnostics" + +#define OT_REST_RESOURCE_PATH_NODE OT_REST_RESOURCE_VERSION "/node" +#define OT_REST_RESOURCE_PATH_NODE_RLOC OT_REST_RESOURCE_PATH_NODE "/rloc" +#define OT_REST_RESOURCE_PATH_NODE_RLOC16 OT_REST_RESOURCE_PATH_NODE "/rloc16" +#define OT_REST_RESOURCE_PATH_NODE_EXTADDRESS OT_REST_RESOURCE_PATH_NODE "/ext-address" +#define OT_REST_RESOURCE_PATH_NODE_STATE OT_REST_RESOURCE_PATH_NODE "/state" +#define OT_REST_RESOURCE_PATH_NODE_NETWORKNAME OT_REST_RESOURCE_PATH_NODE "/network-name" +#define OT_REST_RESOURCE_PATH_NODE_LEADERDATA OT_REST_RESOURCE_PATH_NODE "/leader-data" +#define OT_REST_RESOURCE_PATH_NODE_NUMOFROUTER OT_REST_RESOURCE_PATH_NODE "/num-of-router" +#define OT_REST_RESOURCE_PATH_NODE_EXTPANID OT_REST_RESOURCE_PATH_NODE "/ext-panid" + +#define OT_REST_RESOURCE_PATH_NETWORKS OT_REST_RESOURCE_VERSION "/networks" +#define OT_REST_RESOURCE_PATH_NETWORKS_CURRENT OT_REST_RESOURCE_PATH_NETWORKS "/current" +#define OT_REST_RESOURCE_PATH_NETWORKS_CURRENT_COMMISSION OT_REST_RESOURCE_PATH_NETWORKS_CURRENT "/commission" +#define OT_REST_RESOURCE_PATH_NETWORKS_CURRENT_PREFIX OT_REST_RESOURCE_PATH_NETWORKS_CURRENT "/prefix" #define OT_REST_HTTP_STATUS_200 "200 OK" +#define OT_REST_HTTP_STATUS_400 "400 Bad Request" #define OT_REST_HTTP_STATUS_404 "404 Not Found" #define OT_REST_HTTP_STATUS_405 "405 Method Not Allowed" #define OT_REST_HTTP_STATUS_408 "408 Request Timeout" #define OT_REST_HTTP_STATUS_500 "500 Internal Server Error" +#define OT_REST_405_DESCRIPTION "This method is not allowed" +#define OT_REST_404_DESCRIPTION "Resource is not available, please check the URL" +#define OT_REST_EMPTY_DESCRIPTION "" + +using otbr::Ncp::ControllerOpenThread; using std::chrono::duration_cast; using std::chrono::microseconds; using std::chrono::steady_clock; @@ -62,20 +71,23 @@ using std::placeholders::_1; using std::placeholders::_2; namespace otbr { -namespace rest { +namespace Rest { // MulticastAddr static const char *kMulticastAddrAllRouters = "ff02::2"; -// Default TlvTypes for Diagnostic inforamtion -static const uint8_t kAllTlvTypes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 14, 15, 16, 17, 18, 19}; - // Timeout (in Microseconds) for deleting outdated diagnostics static const uint32_t kDiagResetTimeout = 3000000; // Timeout (in Microseconds) for collecting diagnostics static const uint32_t kDiagCollectTimeout = 2000000; +// Interval (in Microseconds) should wait after a factory reset +static const uint32_t kResetSleepInterval = 1000000; + +// Default timeout (in Seconds) for Joiners +static const uint32_t kDefaultJoinerTimeout = 120; + static std::string GetHttpStatus(HttpStatusCode aErrorCode) { std::string httpStatus; @@ -85,6 +97,9 @@ static std::string GetHttpStatus(HttpStatusCode aErrorCode) case HttpStatusCode::kStatusOk: httpStatus = OT_REST_HTTP_STATUS_200; break; + case HttpStatusCode::kStatusBadRequest: + httpStatus = OT_REST_HTTP_STATUS_400; + break; case HttpStatusCode::kStatusResourceNotFound: httpStatus = OT_REST_HTTP_STATUS_404; break; @@ -108,7 +123,7 @@ Resource::Resource(ControllerOpenThread *aNcp) mInstance = mNcp->GetThreadHelper()->GetInstance(); // Resource Handler - mResourceMap.emplace(OT_REST_RESOURCE_PATH_DIAGNOETIC, &Resource::Diagnostic); + mResourceMap.emplace(OT_REST_RESOURCE_PATH_DIAGNOSTIC, &Resource::Diagnostic); mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE, &Resource::NodeInfo); mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_STATE, &Resource::State); mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_EXTADDRESS, &Resource::ExtendedAddr); @@ -118,9 +133,17 @@ Resource::Resource(ControllerOpenThread *aNcp) mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_NUMOFROUTER, &Resource::NumOfRoute); mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_EXTPANID, &Resource::ExtendedPanId); mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_RLOC, &Resource::Rloc); - - // Resource callback handler - mResourceCallbackMap.emplace(OT_REST_RESOURCE_PATH_DIAGNOETIC, &Resource::HandleDiagnosticCallback); + mResourceMap.emplace(OT_REST_RESOURCE_PATH_NETWORKS, &Resource::Networks); + mResourceMap.emplace(OT_REST_RESOURCE_PATH_NETWORKS_CURRENT, &Resource::CurrentNetwork); + mResourceMap.emplace(OT_REST_RESOURCE_PATH_NETWORKS_CURRENT_PREFIX, &Resource::CurrentNetworkPrefix); + mResourceMap.emplace(OT_REST_RESOURCE_PATH_NETWORKS_CURRENT_COMMISSION, &Resource::CurrentNetworkCommission); + + // Callback Handler + mResourceCallbackMap.emplace(OT_REST_RESOURCE_PATH_DIAGNOSTIC, &Resource::HandleDiagnosticCallback); + mResourceCallbackMap.emplace(OT_REST_RESOURCE_PATH_NETWORKS, &Resource::PostNetworksCallback); + mResourceCallbackMap.emplace(OT_REST_RESOURCE_PATH_NETWORKS_CURRENT, &Resource::PutCurrentNetworkCallback); + mResourceCallbackMap.emplace(OT_REST_RESOURCE_PATH_NETWORKS_CURRENT_COMMISSION, + &Resource::CurrentNetworkCommissionCallback); } void Resource::Init(void) @@ -140,7 +163,7 @@ void Resource::Handle(Request &aRequest, Response &aResponse) const } else { - ErrorHandler(aResponse, HttpStatusCode::kStatusResourceNotFound); + ErrorHandler(aResponse, HttpStatusCode::kStatusResourceNotFound, OT_REST_404_DESCRIPTION); } } @@ -181,57 +204,510 @@ void Resource::HandleDiagnosticCallback(const Request &aRequest, Response &aResp } } -void Resource::ErrorHandler(Response &aResponse, HttpStatusCode aErrorCode) const +void Resource::ErrorHandler(Response &aResponse, HttpStatusCode aErrorCode, std::string aErrorDescription) const { std::string errorMessage = GetHttpStatus(aErrorCode); - std::string body = Json::Error2JsonString(aErrorCode, errorMessage); + std::string body = Json::Error2JsonString(aErrorCode, errorMessage, aErrorDescription); aResponse.SetResponsCode(errorMessage); aResponse.SetBody(body); aResponse.SetComplete(); } -void Resource::GetNodeInfo(Response &aResponse) const +void Resource::CurrentNetworkPrefix(const Request &aRequest, Response &aResponse) const { - otbrError error = OTBR_ERROR_NONE; - struct NodeInfo node; - otRouterInfo routerInfo; - uint8_t maxRouterId; - std::string body; + if (aRequest.GetMethod() == HttpMethod::kPost) + { + PostCurrentNetworkPrefix(aRequest, aResponse); + } + else if (aRequest.GetMethod() == HttpMethod::kGet) + { + GetCurrentNetworkPrefix(aRequest, aResponse); + } + else if (aRequest.GetMethod() == HttpMethod::kDelete) + { + DeleteCurrentNetworkPrefix(aRequest, aResponse); + } + else if (aRequest.GetMethod() == HttpMethod::kOptions) + { + ErrorHandler(aResponse, HttpStatusCode::kStatusOk, OT_REST_EMPTY_DESCRIPTION); + } + else + { + ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed, OT_REST_405_DESCRIPTION); + } +} + +void Resource::GetCurrentNetworkPrefix(const Request &aRequest, Response &aResponse) const +{ + OT_UNUSED_VARIABLE(aRequest); + std::string errorCode; + std::string errorDescription; + std::string body; + otBorderRouterConfig config; + std::vector configList; + otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; + + while (otBorderRouterGetNextOnMeshPrefix(mInstance, &iterator, &config) == OT_ERROR_NONE) + { + configList.push_back(config); + } + body = Json::PrefixList2JsonString(configList); + + aResponse.SetBody(body); + errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); + aResponse.SetResponsCode(errorCode); + aResponse.SetComplete(); +} + +void Resource::PostCurrentNetworkPrefix(const Request &aRequest, Response &aResponse) const +{ + otError err = OT_ERROR_NONE; + std::string errorCode; + std::string errorDescription; + PostError error = PostError::kPostErrorNone; + std::string requestBody; + std::string prefix; + bool defaultRoute; + otBorderRouterConfig config; + + requestBody = aRequest.GetBody(); + + VerifyOrExit(Json::JsonString2Bool(requestBody, std::string("defaultRoute"), defaultRoute), + error = PostError::kPostBadRequest, errorDescription = "Failed at decode json : defaultRoute"); + VerifyOrExit(Json::JsonString2String(requestBody, std::string("prefix"), prefix), + error = PostError::kPostBadRequest, errorDescription = "Failed at decode json : prefix"); + + // Add prefix + memset(&config, 0, sizeof(otBorderRouterConfig)); + + err = otIp6AddressFromString(prefix.c_str(), &config.mPrefix.mPrefix); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostBadRequest, + errorDescription = "Failed at encode prefix :" + std::string(otThreadErrorToString(err))); + + config.mPreferred = true; + config.mSlaac = true; + config.mStable = true; + config.mOnMesh = true; + config.mDefaultRoute = defaultRoute; + config.mPrefix.mLength = 64; + + err = otBorderRouterAddOnMeshPrefix(mInstance, &config); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at add prefix :" + std::string(otThreadErrorToString(err))); + +exit: + + if (error == PostError::kPostErrorNone) + { + aResponse.SetBody(requestBody); + errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); + aResponse.SetResponsCode(errorCode); + aResponse.SetComplete(); + } + else if (error == PostError::kPostSetFail) + { + ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError, errorDescription); + } + else + { + ErrorHandler(aResponse, HttpStatusCode::kStatusBadRequest, errorDescription); + } +} + +void Resource::DeleteCurrentNetworkPrefix(const Request &aRequest, Response &aResponse) const +{ + otError err = OT_ERROR_NONE; + std::string errorCode; + PostError error = PostError::kPostErrorNone; + std::string errorDescription; + std::string requestBody; + std::string value; + struct otIp6Prefix prefix; + + memset(&prefix, 0, sizeof(otIp6Prefix)); + + requestBody = aRequest.GetBody(); + + VerifyOrExit(Json::JsonString2String(requestBody, std::string("prefix"), value), error = PostError::kPostBadRequest, + errorDescription = "Failed at decode json : prefix"); + + err = otIp6AddressFromString(value.c_str(), &prefix.mPrefix); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostBadRequest, + errorDescription = "Failed at encode prefix :" + std::string(otThreadErrorToString(err))); + + prefix.mLength = 64; + + err = otBorderRouterRemoveOnMeshPrefix(mInstance, &prefix); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at delete prefix :" + std::string(otThreadErrorToString(err))); + +exit: + + if (error == PostError::kPostErrorNone) + { + aResponse.SetBody(requestBody); + errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); + aResponse.SetResponsCode(errorCode); + aResponse.SetComplete(); + } + else if (error == PostError::kPostSetFail) + { + ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError, errorDescription); + } + else + { + ErrorHandler(aResponse, HttpStatusCode::kStatusBadRequest, errorDescription); + } +} + +void Resource::CurrentNetworkCommission(const Request &aRequest, Response &aResponse) const +{ + std::string errorCode; + + if (aRequest.GetMethod() == HttpMethod::kPost) + { + PostCurrentNetworkCommission(aRequest, aResponse); + } + + else if (aRequest.GetMethod() == HttpMethod::kOptions) + { + ErrorHandler(aResponse, HttpStatusCode::kStatusOk, OT_REST_EMPTY_DESCRIPTION); + } + else + { + ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed, OT_REST_405_DESCRIPTION); + } +} + +void Resource::CurrentNetworkCommissionCallback(const Request &aRequest, Response &aResponse) +{ + otError err = OT_ERROR_NONE; + std::string errorDescription; + bool complete = true; + std::string errorCode; + PostError error = PostError::kPostErrorNone; + std::string requestBody; + std::string pskd; + unsigned long timeout = kDefaultJoinerTimeout; + const otExtAddress *addrPtr = nullptr; + + auto duration = duration_cast(steady_clock::now() - aResponse.GetStartTime()).count(); + + VerifyOrExit(duration >= kResetSleepInterval, complete = false); + + requestBody = aRequest.GetBody(); + + VerifyOrExit(Json::JsonString2String(requestBody, std::string("pskd"), pskd), error = PostError::kPostBadRequest, + errorDescription = "Failed at decode json: pskd"); + + err = otCommissionerAddJoiner(mInstance, addrPtr, pskd.c_str(), static_cast(timeout)); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostBadRequest, + errorDescription = "Failed at commissioner add joiner: " + std::string(otThreadErrorToString(err))); + +exit: + if (complete) + { + if (error == PostError::kPostErrorNone) + { + aResponse.SetBody(requestBody); + errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); + aResponse.SetResponsCode(errorCode); + aResponse.SetComplete(); + } + else + { + ErrorHandler(aResponse, HttpStatusCode::kStatusBadRequest, errorDescription); + } + } +} + +void Resource::PostCurrentNetworkCommission(const Request &aRequest, Response &aResponse) const +{ + OT_UNUSED_VARIABLE(aRequest); + otError err = OT_ERROR_NONE; + std::string errorDescription; + PostError error = PostError::kPostErrorNone; + + err = otCommissionerStart(mInstance, &Resource::HandleCommissionerStateChanged, &Resource::HandleJoinerEvent, + const_cast(this)); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at commissioner start: " + std::string(otThreadErrorToString(err))); + +exit: + if (error == PostError::kPostErrorNone) + { + aResponse.SetHasCallback(); + + aResponse.SetStartTime(steady_clock::now()); + } + else + { + ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError, errorDescription); + } +} + +void Resource::CurrentNetwork(const Request &aRequest, Response &aResponse) const +{ + std::string errorCode; + + if (aRequest.GetMethod() == HttpMethod::kGet) + { + GetCurrentNetwork(aResponse); + } + else if (aRequest.GetMethod() == HttpMethod::kPut) + { + PutCurrentNetwork(aResponse); + } + else if (aRequest.GetMethod() == HttpMethod::kOptions) + { + ErrorHandler(aResponse, HttpStatusCode::kStatusOk, OT_REST_EMPTY_DESCRIPTION); + } + else + { + ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed, OT_REST_405_DESCRIPTION); + } +} + +void Resource::PutCurrentNetwork(Response &aResponse) const +{ + otInstanceFactoryReset(mInstance); + aResponse.SetHasCallback(); + aResponse.SetStartTime(steady_clock::now()); +} + +void Resource::GetCurrentNetwork(Response &aResponse) const +{ + otError err = OT_ERROR_NONE; + std::string errorDescription; + otbrError error = OTBR_ERROR_NONE; + std::string body; + otOperationalDataset dataset; + std::string errorCode; + + err = otDatasetGetActive(mInstance, &dataset); + VerifyOrExit(err == OT_ERROR_NONE, error = OTBR_ERROR_REST, + errorDescription = "Failed at get active dataset: " + std::string(otThreadErrorToString(err))); + + body = Json::Network2JsonString(dataset); + + aResponse.SetBody(body); + +exit: + + if (error == OTBR_ERROR_NONE) + { + errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); + aResponse.SetResponsCode(errorCode); + } + else + { + ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError, errorDescription); + } +} + +void Resource::PutCurrentNetworkCallback(const Request &aRequest, Response &aResponse) +{ + otError err = OT_ERROR_NONE; + std::string errorDescription; std::string errorCode; + bool complete = true; + PostError error = PostError::kPostErrorNone; + otMasterKey masterKey; + std::string requestBody; + otExtendedPanId extPanid; + long panid; + std::string masterkey; + std::string prefix; + std::string extPanId; + std::string networkName; + std::string panId; + int32_t channel; + char * endptr; + bool defaultRoute; + + otBorderRouterConfig config; - VerifyOrExit(otThreadGetLeaderData(mInstance, &node.mLeaderData) == OT_ERROR_NONE, error = OTBR_ERROR_REST); + auto duration = duration_cast(steady_clock::now() - aResponse.GetStartTime()).count(); - node.mNumOfRouter = 0; - maxRouterId = otThreadGetMaxRouterId(mInstance); - for (uint8_t i = 0; i <= maxRouterId; ++i) + VerifyOrExit(aRequest.GetMethod() == HttpMethod::kPut, complete = false); + + VerifyOrExit(duration >= kResetSleepInterval, complete = false); + + requestBody = aRequest.GetBody(); + + VerifyOrExit(Json::JsonString2String(requestBody, std::string("masterKey"), masterkey), + error = PostError::kPostBadRequest, errorDescription = "Failed at decode json : masterKey"); + VerifyOrExit(Json::JsonString2Bool(requestBody, std::string("defaultRoute"), defaultRoute), + error = PostError::kPostBadRequest, errorDescription = "Failed at decode json : defaultRoute"); + VerifyOrExit(Json::JsonString2String(requestBody, std::string("prefix"), prefix), + error = PostError::kPostBadRequest, errorDescription = "Failed at decode json : prefix"); + VerifyOrExit(Json::JsonString2String(requestBody, std::string("extPanId"), extPanId), + error = PostError::kPostBadRequest, errorDescription = "Failed at decode json : extPanId"); + VerifyOrExit(Json::JsonString2String(requestBody, std::string("networkName"), networkName), + error = PostError::kPostBadRequest, errorDescription = "Failed at decode json : networkName"); + VerifyOrExit(Json::JsonString2Int(requestBody, std::string("channel"), channel), error = PostError::kPostBadRequest, + errorDescription = "Failed at decode json : channel"); + VerifyOrExit(Json::JsonString2String(requestBody, std::string("panId"), panId), error = PostError::kPostBadRequest, + errorDescription = "Failed at decode json : panId"); + VerifyOrExit(otbr::Utils::Hex2Bytes(masterkey.c_str(), masterKey.m8, sizeof(masterKey.m8)) == OT_MASTER_KEY_SIZE, + error = PostError::kPostBadRequest, errorDescription = "Failed at encode masterkey : not valid"); + + // Set Master Key + err = otThreadSetMasterKey(mInstance, &masterKey); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at set masterkey: " + std::string(otThreadErrorToString(err))); + + // Set Network Name + err = otThreadSetNetworkName(mInstance, networkName.c_str()); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at set networkname: " + std::string(otThreadErrorToString(err))); + + // Set Channel + err = otLinkSetChannel(mInstance, channel); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at set channel: " + std::string(otThreadErrorToString(err))); + + // Set ExtPanId + VerifyOrExit(otbr::Utils::Hex2Bytes(extPanId.c_str(), extPanid.m8, sizeof(extPanid)) == sizeof(extPanid), + error = PostError::kPostBadRequest, errorDescription = "Failed at encode extpanid: not valid"); + + err = otThreadSetExtendedPanId(mInstance, &extPanid); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at set extpanid: " + std::string(otThreadErrorToString(err))); + + // Set PanId + panid = strtol(panId.c_str(), &endptr, 0); + VerifyOrExit(*endptr == '\0', error = PostError::kPostBadRequest, + errorDescription = "Failed at check panid: not valid"); + err = otLinkSetPanId(mInstance, static_cast(panid)); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at set panid: " + std::string(otThreadErrorToString(err))); + + // IfConfig Up + err = otIp6SetEnabled(mInstance, true); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at enable Ip6: " + std::string(otThreadErrorToString(err))); + // Thread start + err = otThreadSetEnabled(mInstance, true); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at enable thread : " + std::string(otThreadErrorToString(err))); + + // Add prefix + memset(&config, 0, sizeof(otBorderRouterConfig)); + + err = otIp6AddressFromString(prefix.c_str(), &config.mPrefix.mPrefix); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostBadRequest, + errorDescription = "Failed at encode prefix : " + std::string(otThreadErrorToString(err))); + + config.mPreferred = true; + config.mSlaac = true; + config.mStable = true; + config.mOnMesh = true; + config.mDefaultRoute = defaultRoute; + config.mPrefix.mLength = 64; + + err = otBorderRouterAddOnMeshPrefix(mInstance, &config); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at add prefix : " + std::string(otThreadErrorToString(err))); + +exit: + if (complete) { - if (otThreadGetRouterInfo(mInstance, i, &routerInfo) != OT_ERROR_NONE) + if (error == PostError::kPostErrorNone) { - continue; + aResponse.SetBody(requestBody); + errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); + aResponse.SetResponsCode(errorCode); + aResponse.SetComplete(); + } + else if (error == PostError::kPostSetFail) + { + ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError, errorDescription); + } + else + { + ErrorHandler(aResponse, HttpStatusCode::kStatusBadRequest, errorDescription); } - ++node.mNumOfRouter; } +} + +void Resource::GetNodeInfo(Response &aResponse) const +{ + otError err = OT_ERROR_NONE; + std::string errorDescription; + otbrError error = OTBR_ERROR_NONE; + struct NodeInfo node; + std::string body; + otOperationalDataset dataset; + std::string errorCode; node.mRole = otThreadGetDeviceRole(mInstance); - node.mExtAddress = reinterpret_cast(otLinkGetExtendedAddress(mInstance)); + node.mWpanService = "uninitialized"; + if (node.mRole == OT_DEVICE_ROLE_DISABLED) + { + node.mWpanService = "offline"; + } + else if (node.mRole == OT_DEVICE_ROLE_DETACHED) + { + node.mWpanService = "associating"; + } + else + { + node.mWpanService = "associated"; + } + + // If state is not valid + VerifyOrExit(node.mWpanService == "associated", errorDescription = "Fail at check node state : Unsupported state"); + + // Eui64 + otLinkGetFactoryAssignedIeeeEui64(mInstance, &node.mEui64); + + // MeshLocalAddress + node.mMeshLocalAddress = *otThreadGetMeshLocalEid(mInstance); + + // Version + node.mVersion = otGetVersionString(); + + // MeshLocalPrefix + err = otDatasetGetActive(mInstance, &dataset); + VerifyOrExit(err == OT_ERROR_NONE, error = OTBR_ERROR_REST, + errorDescription = "Failed at get active dataset: " + std::string(otThreadErrorToString(err))); + node.mMeshLocalPrefix = dataset.mMeshLocalPrefix.m8; + + // PanId + node.mPanId = otLinkGetPanId(mInstance); + + // Channel + node.mChannel = otLinkGetChannel(mInstance); + + // Network Name node.mNetworkName = otThreadGetNetworkName(mInstance); - node.mRloc16 = otThreadGetRloc16(mInstance); - node.mExtPanId = reinterpret_cast(otThreadGetExtendedPanId(mInstance)); - node.mRlocAddress = *otThreadGetRloc(mInstance); + + // ExtPanId + node.mExtPanId = reinterpret_cast(otThreadGetExtendedPanId(mInstance)); body = Json::Node2JsonString(node); + aResponse.SetBody(body); exit: if (error == OTBR_ERROR_NONE) { - errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); - aResponse.SetResponsCode(errorCode); + if (node.mWpanService != "associated") + { + ErrorHandler(aResponse, HttpStatusCode::kStatusBadRequest, errorDescription); + } + else + { + errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); + aResponse.SetResponsCode(errorCode); + aResponse.SetComplete(); + } } else { - ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); + ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError, errorDescription); } } @@ -244,7 +720,7 @@ void Resource::NodeInfo(const Request &aRequest, Response &aResponse) const } else { - ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); + ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed, OT_REST_405_DESCRIPTION); } } @@ -269,7 +745,7 @@ void Resource::ExtendedAddr(const Request &aRequest, Response &aResponse) const } else { - ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); + ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed, OT_REST_405_DESCRIPTION); } } @@ -301,7 +777,7 @@ void Resource::State(const Request &aRequest, Response &aResponse) const } else { - ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); + ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed, OT_REST_405_DESCRIPTION); } } @@ -328,18 +804,23 @@ void Resource::NetworkName(const Request &aRequest, Response &aResponse) const } else { - ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); + ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed, OT_REST_405_DESCRIPTION); } } void Resource::GetDataLeaderData(Response &aResponse) const { + otError err = OT_ERROR_NONE; + std::string errorDescription; otbrError error = OTBR_ERROR_NONE; otLeaderData leaderData; std::string body; std::string errorCode; - VerifyOrExit(otThreadGetLeaderData(mInstance, &leaderData) == OT_ERROR_NONE, error = OTBR_ERROR_REST); + err = otThreadGetLeaderData(mInstance, &leaderData); + + VerifyOrExit(err == OT_ERROR_NONE, error = OTBR_ERROR_REST, + errorDescription = "Failed at get leader data: " + std::string(otThreadErrorToString(err))); body = Json::LeaderData2JsonString(leaderData); @@ -353,7 +834,7 @@ void Resource::GetDataLeaderData(Response &aResponse) const } else { - ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); + ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError, errorDescription); } } @@ -366,7 +847,7 @@ void Resource::LeaderData(const Request &aRequest, Response &aResponse) const } else { - ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); + ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed, OT_REST_405_DESCRIPTION); } } @@ -405,7 +886,7 @@ void Resource::NumOfRoute(const Request &aRequest, Response &aResponse) const } else { - ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); + ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed, OT_REST_405_DESCRIPTION); } } @@ -432,14 +913,14 @@ void Resource::Rloc16(const Request &aRequest, Response &aResponse) const } else { - ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); + ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed, OT_REST_405_DESCRIPTION); } } void Resource::GetDataExtendedPanId(Response &aResponse) const { const uint8_t *extPanId = reinterpret_cast(otThreadGetExtendedPanId(mInstance)); - std::string body = Json::Bytes2HexJsonString(extPanId, OT_EXT_PAN_ID_SIZE); + std::string body = Json::Bytes2HexJsonString(extPanId, OT_EXTENDED_PAN_ID_LENGTH); std::string errorCode; aResponse.SetBody(body); @@ -457,7 +938,7 @@ void Resource::ExtendedPanId(const Request &aRequest, Response &aResponse) const } else { - ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); + ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed, OT_REST_405_DESCRIPTION); } } @@ -484,7 +965,212 @@ void Resource::Rloc(const Request &aRequest, Response &aResponse) const } else { - ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); + ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed, OT_REST_405_DESCRIPTION); + } +} + +void Resource::Networks(const Request &aRequest, Response &aResponse) const +{ + std::string errorCode; + + if (aRequest.GetMethod() == HttpMethod::kGet) + { + GetNetworks(aResponse); + } + else if (aRequest.GetMethod() == HttpMethod::kPost) + { + PostNetworks(aResponse); + } + else if (aRequest.GetMethod() == HttpMethod::kOptions) + { + ErrorHandler(aResponse, HttpStatusCode::kStatusOk, OT_REST_EMPTY_DESCRIPTION); + } +} + +void Resource::PostNetworks(Response &aResponse) const +{ + otInstanceFactoryReset(mInstance); + aResponse.SetHasCallback(); + aResponse.SetStartTime(steady_clock::now()); +} + +void Resource::PostNetworksCallback(const Request &aRequest, Response &aResponse) +{ + otError err = OT_ERROR_NONE; + std::string errorDescription; + std::string errorCode; + bool complete = true; + PostError error = PostError::kPostErrorNone; + NetworkConfiguration network; + char * endptr; + long panId; + otPskc pskc; + otMasterKey masterKey; + otExtendedPanId extPanId; + std::string requestBody; + otbr::Psk::Pskc psk; + otBorderRouterConfig config; + char pskcStr[OT_PSKC_LENGTH * 2 + 1]; + uint8_t extPanIdBytes[OT_EXTENDED_PAN_ID_LENGTH]; + + auto duration = duration_cast(steady_clock::now() - aResponse.GetStartTime()).count(); + + VerifyOrExit(aRequest.GetMethod() == HttpMethod::kPost, complete = false); + VerifyOrExit(duration >= kResetSleepInterval, complete = false); + + requestBody = aRequest.GetBody(); + pskcStr[OT_PSKC_LENGTH * 2] = '\0'; + + VerifyOrExit(Json::JsonString2NetworkConfiguration(requestBody, network), error = PostError::kPostBadRequest, + errorDescription = "Failed at decode json : not valid"); + + otbr::Utils::Hex2Bytes(network.mExtPanId.c_str(), extPanIdBytes, OT_EXTENDED_PAN_ID_LENGTH); + otbr::Utils::Bytes2Hex(psk.ComputePskc(extPanIdBytes, network.mNetworkName.c_str(), network.mPassphrase.c_str()), + OT_PSKC_LENGTH, pskcStr); + + VerifyOrExit(otbr::Utils::Hex2Bytes(network.mMasterKey.c_str(), masterKey.m8, sizeof(masterKey.m8)) == + OT_MASTER_KEY_SIZE, + error = PostError::kPostBadRequest, errorDescription = "Failed at encode masterkey : not valid"); + // Set Master Key + err = otThreadSetMasterKey(mInstance, &masterKey); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at set masterkey: " + std::string(otThreadErrorToString(err))); + + // NetworkName + err = otThreadSetNetworkName(mInstance, network.mNetworkName.c_str()); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at set networkname: " + std::string(otThreadErrorToString(err))); + + // Channel + err = otLinkSetChannel(mInstance, network.mChannel); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at set channel: " + std::string(otThreadErrorToString(err))); + + // ExtPanId + + VerifyOrExit(otbr::Utils::Hex2Bytes(network.mExtPanId.c_str(), extPanId.m8, sizeof(extPanId)) == sizeof(extPanId), + error = PostError::kPostBadRequest, errorDescription = "Failed at encode extpanid: not valid"); + err = otThreadSetExtendedPanId(mInstance, &extPanId); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at set extpanid: " + std::string(otThreadErrorToString(err))); + + // PanId + panId = strtol(network.mPanId.c_str(), &endptr, 0); + VerifyOrExit(*endptr == '\0', error = PostError::kPostBadRequest, + errorDescription = "Failed at check panid: not valid"); + err = otLinkSetPanId(mInstance, static_cast(panId)); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at set panid: " + std::string(otThreadErrorToString(err))); + + // pskc + VerifyOrExit(otbr::Utils::Hex2Bytes(pskcStr, pskc.m8, sizeof(pskc)) == sizeof(pskc), + error = PostError::kPostBadRequest, errorDescription = "Failed at encode pskc: not valid"); + err = otThreadSetPskc(mInstance, &pskc); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at set pskc: " + std::string(otThreadErrorToString(err))); + + // IfConfig Up + err = otIp6SetEnabled(mInstance, true); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at enable Ip6: " + std::string(otThreadErrorToString(err))); + + // Thread start + err = otThreadSetEnabled(mInstance, true); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at enable thread: " + std::string(otThreadErrorToString(err))); + + // Add prefix + memset(&config, 0, sizeof(otBorderRouterConfig)); + err = otIp6AddressFromString(network.mPrefix.c_str(), &config.mPrefix.mPrefix); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostBadRequest, + errorDescription = "Failed at encode prefix: " + std::string(otThreadErrorToString(err))); + + config.mPreferred = true; + config.mSlaac = true; + config.mStable = true; + config.mOnMesh = true; + config.mDefaultRoute = network.mDefaultRoute; + config.mPrefix.mLength = 64; + err = otBorderRouterAddOnMeshPrefix(mInstance, &config); + VerifyOrExit(err == OT_ERROR_NONE, error = PostError::kPostSetFail, + errorDescription = "Failed at add prefix: " + std::string(otThreadErrorToString(err))); + +exit: + if (complete) + { + if (error == PostError::kPostErrorNone) + { + aResponse.SetBody(requestBody); + errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); + aResponse.SetResponsCode(errorCode); + aResponse.SetComplete(); + } + else if (error == PostError::kPostSetFail) + { + ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError, errorDescription); + } + else + { + ErrorHandler(aResponse, HttpStatusCode::kStatusBadRequest, errorDescription); + } + } +} + +void Resource::GetNetworks(Response &aResponse) const +{ + aResponse.SetHasCallback(); + aResponse.SetStartTime(steady_clock::now()); + auto threadHelper = mNcp->GetThreadHelper(); + threadHelper->Scan(std::bind(&Resource::NetworksResponseHandler, const_cast(this), &aResponse, _1, _2)); +} + +void Resource::NetworksResponseHandler(Response * aResponse, + otError aError, + const std::vector &aResult) +{ + std::vector results; + std::string body; + std::string errorCode; + + if (aError != OT_ERROR_NONE) + { + ErrorHandler(*aResponse, HttpStatusCode::kStatusInternalServerError, "Scan occurs internal error"); + } + else + { + for (const auto &r : aResult) + { + ActiveScanResult result; + + for (int i = 0; i < OT_EXT_ADDRESS_SIZE; ++i) + { + result.mExtAddress[i] = r.mExtAddress.m8[i]; + } + for (int i = 0; i < OT_EXTENDED_PAN_ID_LENGTH; ++i) + { + result.mExtendedPanId[i] = r.mExtendedPanId.m8[i]; + } + + result.mNetworkName = r.mNetworkName.m8; + result.mSteeringData = + std::vector(r.mSteeringData.m8, r.mSteeringData.m8 + r.mSteeringData.mLength); + result.mPanId = r.mPanId; + result.mJoinerUdpPort = r.mJoinerUdpPort; + result.mChannel = r.mChannel; + result.mRssi = r.mRssi; + result.mLqi = r.mLqi; + result.mVersion = r.mVersion; + result.mIsNative = r.mIsNative; + result.mIsJoinable = r.mIsJoinable; + + results.emplace_back(result); + } + + body = Json::ScanNetworks2JsonString(results); + errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); + aResponse->SetResponsCode(errorCode); + aResponse->SetBody(body); + aResponse->SetComplete(); } } @@ -518,30 +1204,42 @@ void Resource::UpdateDiag(std::string aKey, std::vector &aDiag void Resource::Diagnostic(const Request &aRequest, Response &aResponse) const { - otbrError error = OTBR_ERROR_NONE; + static const uint8_t kAllTlvTypes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 14, 15, 16, 17, 18, 19}; + OT_UNUSED_VARIABLE(aRequest); + otError err = OT_ERROR_NONE; + std::string errorDescription; + otbrError error = OTBR_ERROR_NONE; struct otIp6Address rloc16address = *otThreadGetRloc(mInstance); struct otIp6Address multicastAddress; - VerifyOrExit(otThreadSendDiagnosticGet(mInstance, &rloc16address, kAllTlvTypes, sizeof(kAllTlvTypes)) == - OT_ERROR_NONE, - error = OTBR_ERROR_REST); - VerifyOrExit(otIp6AddressFromString(kMulticastAddrAllRouters, &multicastAddress) == OT_ERROR_NONE, - error = OTBR_ERROR_REST); - VerifyOrExit(otThreadSendDiagnosticGet(mInstance, &multicastAddress, kAllTlvTypes, sizeof(kAllTlvTypes)) == - OT_ERROR_NONE, - error = OTBR_ERROR_REST); + otThreadSetReceiveDiagnosticGetCallback(mInstance, &Resource::DiagnosticResponseHandler, + const_cast(this)); + err = otThreadSendDiagnosticGet(mInstance, &rloc16address, kAllTlvTypes, sizeof(kAllTlvTypes)); + VerifyOrExit(err == OT_ERROR_NONE, error = OTBR_ERROR_REST, + errorDescription = + "Failed at send self diagnostic message: " + std::string(otThreadErrorToString(err))); + + err = otIp6AddressFromString(kMulticastAddrAllRouters, &multicastAddress); + VerifyOrExit(err == OT_ERROR_NONE, error = OTBR_ERROR_REST, + errorDescription = + "Failed at get encode multicast address: " + std::string(otThreadErrorToString(err))); + + err = otThreadSendDiagnosticGet(mInstance, &multicastAddress, kAllTlvTypes, sizeof(kAllTlvTypes)); + VerifyOrExit(err == OT_ERROR_NONE, error = OTBR_ERROR_REST, + errorDescription = + "Failed at send multicast diagnostic message: " + std::string(otThreadErrorToString(err))); exit: if (error == OTBR_ERROR_NONE) { aResponse.SetStartTime(steady_clock::now()); - aResponse.SetCallback(); + aResponse.SetHasCallback(); } else { - ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); + ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError, errorDescription); } } @@ -584,5 +1282,61 @@ void Resource::DiagnosticResponseHandler(otError aError, const otMessage *aMessa } } -} // namespace rest +void Resource::HandleCommissionerStateChanged(otCommissionerState aState, void *aContext) +{ + static_cast(aContext)->HandleCommissionerStateChanged(aState); +} + +void Resource::HandleCommissionerStateChanged(otCommissionerState aState) +{ + switch (aState) + { + case OT_COMMISSIONER_STATE_DISABLED: + otbrLog(OTBR_LOG_INFO, "OTBR-REST: commissioner state disabled"); + break; + case OT_COMMISSIONER_STATE_ACTIVE: + otbrLog(OTBR_LOG_INFO, "OTBR-REST: commissioner state active"); + break; + case OT_COMMISSIONER_STATE_PETITION: + otbrLog(OTBR_LOG_INFO, "OTBR-REST: commissioner state petition"); + break; + } +} + +void Resource::HandleJoinerEvent(otCommissionerJoinerEvent aEvent, + const otJoinerInfo * aJoinerInfo, + const otExtAddress * aJoinerId, + void * aContext) +{ + static_cast(aContext)->HandleJoinerEvent(aEvent, aJoinerInfo, aJoinerId); +} + +void Resource::HandleJoinerEvent(otCommissionerJoinerEvent aEvent, + const otJoinerInfo * aJoinerInfo, + const otExtAddress * aJoinerId) +{ + OT_UNUSED_VARIABLE(aJoinerInfo); + OT_UNUSED_VARIABLE(aJoinerId); + + switch (aEvent) + { + case OT_COMMISSIONER_JOINER_START: + otbrLog(OTBR_LOG_INFO, "OTBR-REST: joiner start"); + break; + case OT_COMMISSIONER_JOINER_CONNECTED: + otbrLog(OTBR_LOG_INFO, "OTBR-REST: joiner connected"); + break; + case OT_COMMISSIONER_JOINER_FINALIZE: + otbrLog(OTBR_LOG_INFO, "OTBR-REST: joiner finalize"); + break; + case OT_COMMISSIONER_JOINER_END: + otbrLog(OTBR_LOG_INFO, "OTBR-REST: joiner end"); + break; + case OT_COMMISSIONER_JOINER_REMOVED: + otbrLog(OTBR_LOG_INFO, "OTBR-REST: joiner remove"); + break; + } +} + +} // namespace Rest } // namespace otbr diff --git a/src/rest/resource.hpp b/src/rest/resource.hpp index 51efcafd92a..74f7057e984 100644 --- a/src/rest/resource.hpp +++ b/src/rest/resource.hpp @@ -44,11 +44,8 @@ #include "rest/request.hpp" #include "rest/response.hpp" -using otbr::Ncp::ControllerOpenThread; -using std::chrono::steady_clock; - namespace otbr { -namespace rest { +namespace Rest { /** * This class implements the Resource handler for OTBR-REST. @@ -63,7 +60,7 @@ class Resource * @param[in] aNcp A pointer to the NCP controller. * */ - Resource(ControllerOpenThread *aNcp); + Resource(otbr::Ncp::ControllerOpenThread *aNcp); /** * This method initialize the Resource handler. @@ -97,13 +94,27 @@ class Resource * * @param[in] aRequest A request instance referred by the Resource handler. * @param[inout] aErrorCode An enum class represents the status code. + * @param[in] aErrorDescription Detailed description of error message. * */ - void ErrorHandler(Response &aResponse, HttpStatusCode aErrorCode) const; + void ErrorHandler(Response &aResponse, HttpStatusCode aErrorCode, std::string aErrorDescription) const; + /** + * This method is a pre-defined callback function used for binding with another callback function defined by + * thread_helper. + * + * + * @param[inout] aResponse A pointer pointing to a response instance. + * @param[in] aError otError represents error type. + * @param[in] aResult Scan result. + * + */ + void NetworksResponseHandler(Response *aResponse, otError aError, const std::vector &aResult); private: typedef void (Resource::*ResourceHandler)(const Request &aRequest, Response &aResponse) const; typedef void (Resource::*ResourceCallbackHandler)(const Request &aRequest, Response &aResponse); + + // RESTful API entry void NodeInfo(const Request &aRequest, Response &aResponse) const; void ExtendedAddr(const Request &aRequest, Response &aResponse) const; void State(const Request &aRequest, Response &aResponse) const; @@ -114,8 +125,24 @@ class Resource void ExtendedPanId(const Request &aRequest, Response &aResponse) const; void Rloc(const Request &aRequest, Response &aResponse) const; void Diagnostic(const Request &aRequest, Response &aResponse) const; - void HandleDiagnosticCallback(const Request &aRequest, Response &aResponse); + void Networks(const Request &aRequest, Response &aResponse) const; + void CurrentNetwork(const Request &aRequest, Response &aResponse) const; + void CurrentNetworkPrefix(const Request &aRequest, Response &aResponse) const; + void CurrentNetworkCommission(const Request &aRequest, Response &aResponse) const; + // Callback Handler + void HandleDiagnosticCallback(const Request &aRequest, Response &aResponse); + void PostNetworksCallback(const Request &aRequest, Response &aResponse); + void PutCurrentNetworkCallback(const Request &aRequest, Response &aResponse); + void CurrentNetworkCommissionCallback(const Request &aRequest, Response &aResponse); + + void PostCurrentNetworkCommission(const Request &aRequest, Response &aResponse) const; + void DeleteCurrentNetworkPrefix(const Request &aRequest, Response &aResponse) const; + void GetCurrentNetworkPrefix(const Request &aRequest, Response &aResponse) const; + void PostCurrentNetworkPrefix(const Request &aRequest, Response &aResponse) const; + void PutCurrentNetwork(Response &aResponse) const; + void GetCurrentNetwork(Response &aResponse) const; + void GetNetworks(Response &aResponse) const; void GetNodeInfo(Response &aResponse) const; void GetDataExtendedAddr(Response &aResponse) const; void GetDataState(Response &aResponse) const; @@ -125,26 +152,45 @@ class Resource void GetDataRloc16(Response &aResponse) const; void GetDataExtendedPanId(Response &aResponse) const; void GetDataRloc(Response &aResponse) const; + void PostNetworks(Response &aResponse) const; + // Methods that manipulate Diagnostic information void DeleteOutDatedDiagnostic(void); void UpdateDiag(std::string aKey, std::vector &aDiag); + static void HandleCommissionerStateChanged(otCommissionerState aState, void *aContext); + + void HandleCommissionerStateChanged(otCommissionerState aState); + + static void HandleJoinerEvent(otCommissionerJoinerEvent aEvent, + const otJoinerInfo * aJoinerInfo, + const otExtAddress * aJoinerId, + void * aContext); + + void HandleJoinerEvent(otCommissionerJoinerEvent aEvent, + const otJoinerInfo * aJoinerInfo, + const otExtAddress * aJoinerId); + static void DiagnosticResponseHandler(otError aError, otMessage * aMessage, const otMessageInfo *aMessageInfo, void * aContext); void DiagnosticResponseHandler(otError aError, const otMessage *aMessage, const otMessageInfo *aMessageInfo); - otInstance * mInstance; - ControllerOpenThread *mNcp; + otInstance * mInstance; + otbr::Ncp::ControllerOpenThread *mNcp; + + // Resource Handler Map + std::unordered_map mResourceMap; - std::unordered_map mResourceMap; + // Reource Handler Map for those need callback std::unordered_map mResourceCallbackMap; + // Map that maintain Diagnostic information std::unordered_map mDiagSet; }; -} // namespace rest +} // namespace Rest } // namespace otbr #endif // OTBR_REST_RESOURCE_HPP_ diff --git a/src/rest/response.cpp b/src/rest/response.cpp index 64208aacd58..b51f8daebcd 100644 --- a/src/rest/response.cpp +++ b/src/rest/response.cpp @@ -35,16 +35,18 @@ #define OT_REST_RESPONSE_ACCESS_CONTROL_ALLOW_HEADERS \ "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, " \ "Access-Control-Request-Headers" -#define OT_REST_RESPONSE_ACCESS_CONTROL_ALLOW_METHOD "GET" +#define OT_REST_RESPONSE_ACCESS_CONTROL_ALLOW_METHOD "*" + +using std::chrono::steady_clock; namespace otbr { -namespace rest { +namespace Rest { Response::Response(void) : mCallback(false) , mComplete(false) { - // HTTP protocol + // HTTP protocol 1.1 mProtocol = "HTTP/1.1 "; // Pre-defined headers @@ -86,7 +88,7 @@ void Response::SetResponsCode(std::string &aCode) mCode = aCode; } -void Response::SetCallback(void) +void Response::SetHasCallback(void) { mCallback = true; } @@ -122,5 +124,5 @@ std::string Response::Serialize(void) const return ret; } -} // namespace rest +} // namespace Rest } // namespace otbr diff --git a/src/rest/response.hpp b/src/rest/response.hpp index 597a732167c..60a06cf832f 100644 --- a/src/rest/response.hpp +++ b/src/rest/response.hpp @@ -40,13 +40,8 @@ #include "rest/types.hpp" -using std::chrono::duration_cast; -using std::chrono::microseconds; -using std::chrono::seconds; -using std::chrono::steady_clock; - namespace otbr { -namespace rest { +namespace Rest { /** * This class implements a response class for OTBR_REST, it could be manipulated by connection instance and resource @@ -91,7 +86,7 @@ class Response * * */ - void SetCallback(void); + void SetHasCallback(void); /** * This method checks whether this response need to be processed by callback handler later. @@ -119,14 +114,14 @@ class Response * * @param[in] aStartTime A timestamp indicates when the response start to wait for callback. */ - void SetStartTime(steady_clock::time_point aStartTime); + void SetStartTime(std::chrono::steady_clock::time_point aStartTime); /** * This method returns a timestamp of start time. * * @returns A timepoint object indicates start time. */ - steady_clock::time_point GetStartTime() const; + std::chrono::steady_clock::time_point GetStartTime() const; /** * This method serialize a response to a string that could be sent by socket later. @@ -136,17 +131,17 @@ class Response std::string Serialize(void) const; private: - bool mCallback; - std::vector mHeaderField; - std::vector mHeaderValue; - std::string mCode; - std::string mProtocol; - std::string mBody; - bool mComplete; - steady_clock::time_point mStartTime; + bool mCallback; + std::vector mHeaderField; + std::vector mHeaderValue; + std::string mCode; + std::string mProtocol; + std::string mBody; + bool mComplete; + std::chrono::steady_clock::time_point mStartTime; }; -} // namespace rest +} // namespace Rest } // namespace otbr #endif // OTBR_REST_RESPONSE_HPP_ diff --git a/src/rest/rest_web_server.cpp b/src/rest/rest_web_server.cpp index a9c7964b481..920bdf8b16e 100644 --- a/src/rest/rest_web_server.cpp +++ b/src/rest/rest_web_server.cpp @@ -32,15 +32,15 @@ #include -using std::chrono::duration_cast; -using std::chrono::microseconds; +using otbr::Ncp::ControllerOpenThread; using std::chrono::steady_clock; namespace otbr { -namespace rest { +namespace Rest { // Maximum number of connection a server support at the same time. static const uint32_t kMaxServeNum = 500; + // Port number used by Rest server. static const uint32_t kPortNumber = 8081; @@ -83,7 +83,6 @@ void RestWebServer::UpdateFdSet(otSysMainloopContext &aMainloop) connection->UpdateFdSet(aMainloop); } exit: - return; } @@ -201,7 +200,6 @@ otbrError RestWebServer::Accept(int aListenFd) } otbrLog(OTBR_LOG_ERR, "rest server accept error: %s %s", errorMessage.c_str(), strerror(err)); } - return error; } @@ -236,5 +234,5 @@ bool RestWebServer::SetFdNonblocking(int32_t fd) return ret; } -} // namespace rest +} // namespace Rest } // namespace otbr diff --git a/src/rest/rest_web_server.hpp b/src/rest/rest_web_server.hpp index 3b7319c0e70..4b9b8486060 100644 --- a/src/rest/rest_web_server.hpp +++ b/src/rest/rest_web_server.hpp @@ -36,11 +36,8 @@ #include "rest/connection.hpp" -using otbr::Ncp::ControllerOpenThread; -using std::chrono::steady_clock; - namespace otbr { -namespace rest { +namespace Rest { /** * This class implements a REST server. @@ -54,10 +51,10 @@ class RestWebServer * * @param[in] aNcp A pointer to the NCP controller. * - * @returns A pointer pointing to the static rest server instance. + * @returns A pointer to the static rest server instance. * */ - static RestWebServer *GetRestWebServer(ControllerOpenThread *aNcp); + static RestWebServer *GetRestWebServer(otbr::Ncp::ControllerOpenThread *aNcp); /** * This method initializes the REST server. @@ -85,7 +82,7 @@ class RestWebServer otbrError Process(otSysMainloopContext &aMainloop); private: - RestWebServer(ControllerOpenThread *aNcp); + RestWebServer(otbr::Ncp::ControllerOpenThread *aNcp); otbrError UpdateConnections(fd_set &aReadFdSet); void CreateNewConnection(int32_t &aFd); otbrError Accept(int32_t aListenFd); @@ -94,15 +91,18 @@ class RestWebServer // Resource handler Resource mResource; + // Struct for server configuration sockaddr_in mAddress; + // File descriptor for listening int32_t mListenFd; + // Connection List std::unordered_map> mConnectionSet; }; -} // namespace rest +} // namespace Rest } // namespace otbr #endif // OTBR_REST_REST_WEB_SERVER_HPP_ diff --git a/src/rest/types.hpp b/src/rest/types.hpp index c20d9c16990..8681263fde9 100644 --- a/src/rest/types.hpp +++ b/src/rest/types.hpp @@ -40,10 +40,8 @@ #include "openthread/netdiag.h" -using std::chrono::steady_clock; - namespace otbr { -namespace rest { +namespace Rest { enum class HttpMethod : std::uint8_t { @@ -58,11 +56,12 @@ enum class HttpMethod : std::uint8_t enum class HttpStatusCode : std::uint16_t { - kStatusOk = 200, - kStatusResourceNotFound = 404, - kStatusMethodNotAllowed = 405, - kStatusRequestTimeout = 408, - kStatusInternalServerError = 500, + kStatusOk = 200, ///< OK + kStatusBadRequest = 400, ///< Bad Request + kStatusResourceNotFound = 404, ///< Resource Not Found + kStatusMethodNotAllowed = 405, ///< Method Not Allowed + kStatusRequestTimeout = 408, ///< Request Timeout + kStatusInternalServerError = 500, ///< Internal Server Error }; enum class PostError : std::uint8_t @@ -84,25 +83,62 @@ enum class ConnectionState : std::uint8_t kComplete = 7, ///< No longer need to be processed }; + +struct ActiveScanResult +{ + uint8_t mExtAddress[OT_EXT_ADDRESS_SIZE + 1]; ///< IEEE 802.15.4 Extended Address + std::string mNetworkName; ///< Thread Network Name + uint8_t mExtendedPanId[OT_EXT_PAN_ID_SIZE + 1]; ///< Thread Extended PAN ID + std::vector mSteeringData; ///< Steering Data + uint16_t mPanId; ///< IEEE 802.15.4 PAN ID + uint16_t mJoinerUdpPort; ///< Joiner UDP Port + uint8_t mChannel; ///< IEEE 802.15.4 Channel + int8_t mRssi; ///< RSSI (dBm) + uint8_t mLqi; ///< LQI + uint8_t mVersion; ///< Version + bool mIsNative; ///< Native Commissioner flag + bool mIsJoinable; ///< Joining Permitted flag +}; + +struct NetworkConfiguration +{ + bool mDefaultRoute; + uint8_t mChannel; + std::string mMasterKey; + std::string mPrefix; + std::string mNetworkName; + std::string mPanId; + std::string mPassphrase; + std::string mExtPanId; +}; + struct NodeInfo { + std::string mWpanService; uint32_t mRole; - uint32_t mNumOfRouter; - uint16_t mRloc16; + uint16_t mPanId; + uint8_t mChannel; + otExtAddress mEui64; const uint8_t *mExtPanId; - const uint8_t *mExtAddress; - otIp6Address mRlocAddress; - otLeaderData mLeaderData; + const uint8_t *mMeshLocalPrefix; + otIp6Address mMeshLocalAddress; std::string mNetworkName; + std::string mVersion; }; struct DiagInfo { - steady_clock::time_point mStartTime; - std::vector mDiagContent; + std::chrono::steady_clock::time_point mStartTime; + std::vector mDiagContent; +}; + +struct NetworksInfo +{ + std::chrono::steady_clock::time_point mStartTime; + otActiveScanResult mNetworkContent; }; -} // namespace rest +} // namespace Rest } // namespace otbr #endif // OTBR_REST_TYPES_HPP_ diff --git a/src/web/CMakeLists.txt b/src/web/CMakeLists.txt deleted file mode 100644 index 0b755129aac..00000000000 --- a/src/web/CMakeLists.txt +++ /dev/null @@ -1,83 +0,0 @@ -# -# Copyright (c) 2020, The OpenThread Authors. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the copyright holder nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -add_executable(otbr-web - main.cpp - web-service/ot_client.cpp - web-service/web_server.cpp - web-service/wpan_service.cpp -) -target_compile_definitions(otbr-web PRIVATE - WEB_FILE_PATH=\"${OTBR_WEB_DATADIR}/frontend\" -) -# TODO remove this when Simple Http Server is replaced and web-gui is refactored -target_compile_options(otbr-web PRIVATE - -Wno-deprecated-declarations - -Wno-unused-lambda-capture -) -target_include_directories(otbr-web PRIVATE - ${JSONCPP_INCLUDE_DIRS} - ${Boost_INCLUDE_DIRS} - ${PROJECT_SOURCE_DIR}/third_party/Simple-web-server/repo -) -target_link_libraries(otbr-web PRIVATE - $<$:-L$> - ${JSONCPP_LIBRARIES} - otbr-common - otbr-utils - openthread-ftd - openthread-posix - mbedtls - ${Boost_LIBRARIES} - pthread -) -install( - TARGETS otbr-web - DESTINATION sbin -) - -add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/angular ${CMAKE_CURRENT_BINARY_DIR}/angular) -add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/angular-material ${CMAKE_CURRENT_BINARY_DIR}/angular-material) -add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/mdl ${CMAKE_CURRENT_BINARY_DIR}/mdl) -add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/d3js ${CMAKE_CURRENT_BINARY_DIR}/d3js) - -install(DIRECTORY web-service/frontend - DESTINATION ${OTBR_WEB_DATADIR} -) - -if(OTBR_SYSTEMD_UNIT_DIR) - configure_file(otbr-web.service.in otbr-web.service) - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/otbr-web.service - DESTINATION ${OTBR_SYSTEMD_UNIT_DIR} - ) -else() - configure_file(otbr-web.init.in otbr-web.init) - install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/otbr-web.init - DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/init.d - RENAME otbr-web) -endif() diff --git a/src/web/main.cpp b/src/web/main.cpp deleted file mode 100644 index 895a54b4da5..00000000000 --- a/src/web/main.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2017, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * This file is the entry of the program, it starts a Web service. - */ -#define OT_HTTP_PORT 80 - -#include "openthread-br/config.h" - -#include -#include -#include -#include -#include -#include - -#include "common/code_utils.hpp" -#include "common/logging.hpp" -#include "web/web-service/web_server.hpp" - -static const char kSyslogIdent[] = "otbr-web"; -static const char kDefaultInterfaceName[] = "wpan0"; -static const char kDefaultListenAddr[] = "0.0.0.0"; - -std::unique_ptr sServer(nullptr); - -static void HandleSignal(int aSignal) -{ - signal(aSignal, SIG_DFL); - - otbrLog(OTBR_LOG_CRIT, "Stopping web server"); - - if (sServer != nullptr) - { - sServer->StopWebServer(); - } -} - -static void PrintVersion(void) -{ - printf("%s\n", OTBR_PACKAGE_VERSION); -} - -int main(int argc, char **argv) -{ - const char *interfaceName = nullptr; - const char *httpListenAddr = nullptr; - const char *httpPort = nullptr; - int logLevel = OTBR_LOG_INFO; - int ret = 0; - int opt; - uint16_t port = OT_HTTP_PORT; - - while ((opt = getopt(argc, argv, "d:I:p:va:")) != -1) - { - switch (opt) - { - case 'a': - httpListenAddr = optarg; - break; - case 'd': - logLevel = atoi(optarg); - break; - case 'I': - interfaceName = optarg; - break; - - case 'p': - httpPort = optarg; - VerifyOrExit(httpPort != nullptr); - port = atoi(httpPort); - break; - - case 'v': - PrintVersion(); - ExitNow(); - break; - - default: - fprintf(stderr, "Usage: %s [-d DEBUG_LEVEL] [-I interfaceName] [-p port] [-a listenAddress] [-v]\n", - argv[0]); - ExitNow(ret = -1); - break; - } - } - - otbrLogInit(kSyslogIdent, logLevel, true); - otbrLog(OTBR_LOG_INFO, "Running %s", OTBR_PACKAGE_VERSION); - - if (interfaceName == nullptr) - { - interfaceName = kDefaultInterfaceName; - printf("interfaceName not specified, using default %s\n", interfaceName); - } - - if (httpListenAddr == nullptr) - { - httpListenAddr = kDefaultListenAddr; - printf("listenAddr not specified, using default %s\n", httpListenAddr); - } - - if (httpPort == nullptr) - { - printf("http port not specified, using default %d\n", port); - } - - otbrLog(OTBR_LOG_INFO, "border router web started on %s", interfaceName); - - // allow quitting elegantly - signal(SIGTERM, HandleSignal); - signal(SIGINT, HandleSignal); - - sServer.reset(new otbr::Web::WebServer()); - sServer->StartWebServer(interfaceName, httpListenAddr, port); - - otbrLogDeinit(); - -exit: - return ret; -} diff --git a/src/web/otbr-web.init.in b/src/web/otbr-web.init.in deleted file mode 100755 index be901d023e0..00000000000 --- a/src/web/otbr-web.init.in +++ /dev/null @@ -1,102 +0,0 @@ -#!/bin/sh -# Copyright (c) 2018, The OpenThread Authors. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the copyright holder nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -### BEGIN INIT INFO -# Provides: otbr-web -# Required-Start: otbr-agent -# Required-Stop: -# Should-Start: -# Should-Stop: -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: web server for boreder router -# Description: otbr-web provides a web gui for boder router -### END INIT INFO - -set -e - -PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin -DESC="thread web interface" -NAME=otbr-web -DAEMON=@CMAKE_INSTALL_FULL_SBINDIR@/otbr-web -PIDFILE=/var/run/otbr-web.pid - -OTBR_WEB_CONF=@CMAKE_INSTALL_FULL_SYSCONFDIR@/default/otbr-web - -. /lib/lsb/init-functions -. /lib/init/vars.sh - -if [ -f $OTBR_WEB_CONF ]; then - . $OTBR_WEB_CONF -fi - -start_web() -{ - if [ -e $PIDFILE ]; then - if $0 status > /dev/null ; then - log_success_msg "$DESC already started; not starting." - return - else - log_success_msg "Removing stale PID file $PIDFILE." - rm -f $PIDFILE - fi - fi - - log_daemon_msg "Starting $DESC" "$NAME" - start-stop-daemon --start --quiet \ - --pidfile $PIDFILE --make-pidfile \ - -b --exec $DAEMON -- $OTBR_WEB_OPTS - log_end_msg $? -} - -stop_web() -{ - log_daemon_msg "Stopping $DESC" "$NAME" - start-stop-daemon --stop --retry 5 --quiet --oknodo \ - --pidfile $PIDFILE --remove-pidfile - log_end_msg $? -} - -case "$1" in - start) - start_web - ;; - restart|reload|force-reload) - stop_web - start_web - ;; - stop|force-stop) - stop_web - ;; - status) - status_of_proc -p $PIDFILE $DAEMON $NAME && exit 0 || exit $? - ;; - *) - log_action_msg "Usage: /etc/init.d/$NAME {start|stop|status|restart|reload|force-reload}" - exit 2 - ;; -esac diff --git a/src/web/otbr-web.service.in b/src/web/otbr-web.service.in deleted file mode 100644 index 575e3255bf7..00000000000 --- a/src/web/otbr-web.service.in +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=Border Router Web -After=otbr-agent.service -ConditionPathExists=@CMAKE_INSTALL_FULL_SBINDIR@/otbr-web - -[Service] -EnvironmentFile=-@CMAKE_INSTALL_FULL_SYSCONFDIR@/default/otbr-web -ExecStart=@CMAKE_INSTALL_FULL_SBINDIR@/otbr-web $OTBR_WEB_OPTS -Restart=on-failure -RestartSec=5 -RestartPreventExitStatus=SIGKILL - -[Install] -WantedBy=multi-user.target -Alias=otbr-web.service diff --git a/src/web/web-service/ot_client.cpp b/src/web/web-service/ot_client.cpp deleted file mode 100644 index f151a01262f..00000000000 --- a/src/web/web-service/ot_client.cpp +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (c) 2020, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "web/web-service/ot_client.hpp" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common/code_utils.hpp" -#include "common/logging.hpp" -#include "utils/strcpy_utils.hpp" - -// Temporary solution before posix platform header files are cleaned up. -#ifndef OPENTHREAD_POSIX_APP_SOCKET_NAME -#define OPENTHREAD_POSIX_APP_SOCKET_NAME "/tmp/openthread.sock" -#endif - -namespace otbr { -namespace Web { - -OpenThreadClient::OpenThreadClient(void) - : mTimeout(kDefaultTimeout) - , mSocket(-1) -{ -} - -OpenThreadClient::~OpenThreadClient(void) -{ - Disconnect(); -} - -void OpenThreadClient::Disconnect(void) -{ - if (mSocket != -1) - { - close(mSocket); - mSocket = -1; - } -} - -bool OpenThreadClient::Connect(void) -{ - struct sockaddr_un sockname; - int ret; - - mSocket = socket(AF_UNIX, SOCK_STREAM, 0); - VerifyOrExit(mSocket != -1, perror("socket"); ret = EXIT_FAILURE); - - memset(&sockname, 0, sizeof(struct sockaddr_un)); - sockname.sun_family = AF_UNIX; - strcpy_safe(sockname.sun_path, sizeof(sockname.sun_path), OPENTHREAD_POSIX_APP_SOCKET_NAME); - - ret = connect(mSocket, reinterpret_cast(&sockname), sizeof(struct sockaddr_un)); - - if (ret == -1) - { - otbrLog(OTBR_LOG_ERR, "OpenThread daemon is not running."); - } - -exit: - return ret == 0; -} - -char *OpenThreadClient::Execute(const char *aFormat, ...) -{ - va_list args; - int ret; - char * rval = nullptr; - ssize_t count; - size_t rxLength = 0; - - va_start(args, aFormat); - ret = vsnprintf(&mBuffer[1], sizeof(mBuffer) - 1, aFormat, args); - va_end(args); - - if (ret < 0) - { - otbrLog(OTBR_LOG_ERR, "Failed to generate command: %s", strerror(errno)); - } - - mBuffer[0] = '\n'; - ret++; - - if (ret == sizeof(mBuffer)) - { - otbrLog(OTBR_LOG_ERR, "Command exceeds maximum limit: %d", kBufferSize); - } - - mBuffer[ret] = '\n'; - ret++; - - count = write(mSocket, mBuffer, ret); - - if (count < ret) - { - mBuffer[ret] = '\0'; - otbrLog(OTBR_LOG_ERR, "Failed to send command: %s", mBuffer); - } - - for (int i = 0; i < mTimeout; ++i) - { - fd_set readFdSet; - timeval timeout = {0, 1000}; - char * done; - - FD_ZERO(&readFdSet); - FD_SET(mSocket, &readFdSet); - - ret = select(mSocket + 1, &readFdSet, nullptr, nullptr, &timeout); - VerifyOrExit(ret != -1 || errno == EINTR); - if (ret <= 0) - { - continue; - } - - count = read(mSocket, &mBuffer[rxLength], sizeof(mBuffer) - rxLength); - VerifyOrExit(count > 0); - rxLength += count; - - mBuffer[rxLength] = '\0'; - done = strstr(mBuffer, "Done\r\n"); - - if (done != nullptr) - { - // remove trailing \r\n - if (done - rval > 2) - { - done[-2] = '\0'; - } - - rval = mBuffer; - break; - } - } - -exit: - return rval; -} - -int OpenThreadClient::Scan(WpanNetworkInfo *aNetworks, int aLength) -{ - char *result; - int rval = 0; - - mTimeout = 5000; - result = Execute("scan"); - VerifyOrExit(result != nullptr); - - for (result = strtok(result, "\r\n"); result != nullptr && rval < aLength; result = strtok(nullptr, "\r\n")) - { - static const char kCliPrompt[] = "> "; - char * cliPrompt; - int matched; - int joinable; - int lqi; - - // remove prompt - if ((cliPrompt = strstr(result, kCliPrompt)) != nullptr) - { - if (cliPrompt == result) - { - result += sizeof(kCliPrompt) - 1; - } - else - { - memmove(cliPrompt, cliPrompt + sizeof(kCliPrompt) - 1, strlen(cliPrompt) - sizeof(kCliPrompt) - 1); - } - } - - matched = sscanf(result, - "| %d | %s | %" PRIx64 - " | %hx | %02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx | %hu | %hhd | %d |", - &joinable, aNetworks[rval].mNetworkName, &aNetworks[rval].mExtPanId, &aNetworks[rval].mPanId, - &aNetworks[rval].mHardwareAddress[0], &aNetworks[rval].mHardwareAddress[1], - &aNetworks[rval].mHardwareAddress[2], &aNetworks[rval].mHardwareAddress[3], - &aNetworks[rval].mHardwareAddress[4], &aNetworks[rval].mHardwareAddress[5], - &aNetworks[rval].mHardwareAddress[6], &aNetworks[rval].mHardwareAddress[7], - &aNetworks[rval].mChannel, &aNetworks[rval].mRssi, &lqi); - - // 15 is the number of output arguments of the last sscanf() - if (matched != 15) - { - continue; - } - - aNetworks[rval].mAllowingJoin = joinable != 0; - ++rval; - } - - mTimeout = kDefaultTimeout; - -exit: - return rval; -} - -bool OpenThreadClient::FactoryReset(void) -{ - const char *result; - bool rval = false; -#if __APPLE__ - typedef sig_t sighandler_t; -#endif - sighandler_t handler; - - // Ignore the expected SIGPIPE signal during daemon reset. - handler = signal(SIGPIPE, SIG_IGN); - Execute("factoryreset"); - signal(SIGPIPE, handler); - Disconnect(); - sleep(1); - VerifyOrExit(rval = Connect()); - - result = Execute("version"); - VerifyOrExit(result != nullptr); - - rval = strstr(result, "OPENTHREAD") != nullptr; - -exit: - return rval; -} - -} // namespace Web -} // namespace otbr diff --git a/src/web/web-service/ot_client.hpp b/src/web/web-service/ot_client.hpp deleted file mode 100644 index c70513092c7..00000000000 --- a/src/web/web-service/ot_client.hpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2019, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef OTBR_WEB_WEB_SERVICE_OT_CLIENT_HPP_ -#define OTBR_WEB_WEB_SERVICE_OT_CLIENT_HPP_ - -#include "openthread-br/config.h" - -#include - -namespace otbr { -namespace Web { - -#define OT_SCANNED_NET_BUFFER_SIZE 250 -#define OT_SET_MAX_DATA_SIZE 250 -#define OT_NETWORK_NAME_MAX_SIZE 17 -#define OT_HARDWARE_ADDRESS_SIZE 8 -#define OT_PREFIX_SIZE 8 -#define OT_ROUTER_ROLE 2 - -struct WpanNetworkInfo -{ - char mNetworkName[OT_NETWORK_NAME_MAX_SIZE]; - bool mAllowingJoin; - uint16_t mPanId; - uint16_t mChannel; - uint64_t mExtPanId; - int8_t mRssi; - uint8_t mHardwareAddress[OT_HARDWARE_ADDRESS_SIZE]; - uint8_t mPrefix[OT_PREFIX_SIZE]; -}; - -/** - * This class implements functionality of OpenThread client. - * - */ -class OpenThreadClient -{ -public: - /** - * This constructor creates an OpenThread client. - * - */ - OpenThreadClient(void); - /** - * This destructor destories an OpenThread client. - * - */ - ~OpenThreadClient(void); - - /** - * This method connects to OpenThread daemon. - * - * @retval true Successfully connected to the daemon. - * @retval false Failed to connected to the daemon. - * - */ - bool Connect(void); - - /** - * This method executes OpenThread CLI. - * - * @param[in] aFormat C style format string. - * @param[in] ... C style format arguments. - * - * @returns A pointer to the output if succeeded, otherwise nullptr. - * - */ - char *Execute(const char *aFormat, ...); - - /** - * This method scans Thread network. - * - * @param[out] aNetworks A pointer to the buffer to store network information. - * @param[in] aLength Number of entries in @p aNetworks. - * - * @returns Number of entries found. 0 if none found. - * - */ - int Scan(WpanNetworkInfo *aNetworks, int aLength); - - /** - * This method performs factory reset. - * - */ - bool FactoryReset(void); - -private: - void Disconnect(void); - - enum - { - kBufferSize = 1024, ///< Maximum command line input and output buffer. - kDefaultTimeout = 800, ///< Default timeout(ms) waiting for a command finish. - }; - - char mBuffer[kBufferSize]; - int mTimeout; /// Timeout in milliseconds - int mSocket; -}; - -} // namespace Web -} // namespace otbr - -#endif // OTBR_WEB_WEB_SERVICE_OT_CLIENT_HPP_ diff --git a/src/web/web-service/web_server.cpp b/src/web/web-service/web_server.cpp deleted file mode 100644 index d99cc01c823..00000000000 --- a/src/web/web-service/web_server.cpp +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright (c) 2017, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * This file implements the web server of border router - */ - -#include "web/web-service/web_server.hpp" - -#define BOOST_NO_CXX11_SCOPED_ENUMS -#include -#undef BOOST_NO_CXX11_SCOPED_ENUMS - -#include - -#define OT_ADD_PREFIX_PATH "^/add_prefix" -#define OT_AVAILABLE_NETWORK_PATH "^/available_network$" -#define OT_DELETE_PREFIX_PATH "^/delete_prefix" -#define OT_FORM_NETWORK_PATH "^/form_network$" -#define OT_GET_NETWORK_PATH "^/get_properties$" -#define OT_JOIN_NETWORK_PATH "^/join_network$" -#define OT_SET_NETWORK_PATH "^/settings$" -#define OT_COMMISSIONER_START_PATH "^/commission$" -#define OT_REQUEST_METHOD_GET "GET" -#define OT_REQUEST_METHOD_POST "POST" -#define OT_RESPONSE_SUCCESS_STATUS "HTTP/1.1 200 OK\r\n" -#define OT_RESPONSE_HEADER_LENGTH "Content-Length: " -#define OT_RESPONSE_HEADER_CSS_TYPE "\r\nContent-Type: text/css" -#define OT_RESPONSE_HEADER_TYPE "Content-Type: application/json\r\n charset=utf-8" -#define OT_RESPONSE_PLACEHOLD "\r\n\r\n" -#define OT_RESPONSE_FAILURE_STATUS "HTTP/1.1 400 Bad Request\r\n" -#define OT_BUFFER_SIZE 1024 - -namespace otbr { -namespace Web { - -static void EscapeHtml(std::string &content) -{ - std::string output; - - output.reserve(content.size()); - for (char c : content) - { - switch (c) - { - case '&': - output.append("&"); - break; - case '<': - output.append("<"); - break; - case '>': - output.append(">"); - break; - case '"': - output.append("""); - break; - case '\'': - output.append("'"); - break; - default: - output.push_back(c); - break; - } - } - - output.swap(content); -} - -WebServer::WebServer(void) - : mServer(new HttpServer()) -{ -} - -WebServer::~WebServer(void) -{ - delete mServer; -} - -void WebServer::Init() -{ - std::string networkName, extPanId; - - if (mWpanService.GetWpanServiceStatus(networkName, extPanId) > 0) - { - return; - } -} - -void WebServer::StartWebServer(const char *aIfName, const char *aListenAddr, uint16_t aPort) -{ - if (aListenAddr != nullptr) - { - mServer->config.address = aListenAddr; - } - mServer->config.port = aPort; - mWpanService.SetInterfaceName(aIfName); - Init(); - ResponseJoinNetwork(); - ResponseFormNetwork(); - ResponseAddOnMeshPrefix(); - ResponseDeleteOnMeshPrefix(); - ResponseGetStatus(); - ResponseGetAvailableNetwork(); - ResponseCommission(); - DefaultHttpResponse(); - mServer->start(); -} - -void WebServer::StopWebServer(void) -{ - mServer->stop(); -} - -void WebServer::HandleHttpRequest(const char *aUrl, const char *aMethod, HttpRequestCallback aCallback) -{ - mServer->resource[aUrl][aMethod] = [aCallback, this](std::shared_ptr response, - std::shared_ptr request) { - try - { - std::string httpResponse; - if (aCallback != nullptr) - { - httpResponse = aCallback(request->content.string(), this); - } - - *response << OT_RESPONSE_SUCCESS_STATUS << OT_RESPONSE_HEADER_LENGTH << httpResponse.length() - << OT_RESPONSE_PLACEHOLD << httpResponse; - } catch (std::exception &e) - { - std::string content = e.what(); - EscapeHtml(content); - *response << OT_RESPONSE_FAILURE_STATUS << OT_RESPONSE_HEADER_LENGTH << strlen(e.what()) - << OT_RESPONSE_PLACEHOLD << content; - } - }; -} - -void DefaultResourceSend(const HttpServer & aServer, - const std::shared_ptr &aResponse, - const std::shared_ptr & aIfStream) -{ - static std::vector buffer(OT_BUFFER_SIZE); // Safe when server is running on one thread - - std::streamsize readLength; - - if ((readLength = aIfStream->read(&buffer[0], buffer.size()).gcount()) > 0) - { - aResponse->write(&buffer[0], readLength); - if (readLength == static_cast(buffer.size())) - { - aServer.send(aResponse, [&aServer, aResponse, aIfStream](const boost::system::error_code &ec) { - if (!ec) - { - DefaultResourceSend(aServer, aResponse, aIfStream); - } - else - { - std::cerr << "Connection interrupted" << std::endl; - } - }); - } - } -} - -void WebServer::DefaultHttpResponse(void) -{ - mServer->default_resource[OT_REQUEST_METHOD_GET] = [this](std::shared_ptr response, - std::shared_ptr request) { - try - { - auto webRootPath = boost::filesystem::canonical(WEB_FILE_PATH); - auto path = boost::filesystem::canonical(webRootPath / request->path); - // Check if path is within webRootPath - if (std::distance(webRootPath.begin(), webRootPath.end()) > std::distance(path.begin(), path.end()) || - !std::equal(webRootPath.begin(), webRootPath.end(), path.begin())) - { - throw std::invalid_argument("path must be within root path"); - } - if (boost::filesystem::is_directory(path)) - { - path /= "index.html"; - } - if (!(boost::filesystem::exists(path) && boost::filesystem::is_regular_file(path))) - { - throw std::invalid_argument("file does not exist"); - } - - std::string cacheControl, etag; - - auto ifs = std::make_shared(); - ifs->open(path.string(), std::ifstream::in | std::ios::binary | std::ios::ate); - std::string extension = boost::filesystem::extension(path.string()); - std::string style = ""; - if (extension == ".css") - { - style = OT_RESPONSE_HEADER_CSS_TYPE; - } - - if (*ifs) - { - auto length = ifs->tellg(); - ifs->seekg(0, std::ios::beg); - - *response << OT_RESPONSE_SUCCESS_STATUS << cacheControl << etag << OT_RESPONSE_HEADER_LENGTH << length - << style << OT_RESPONSE_PLACEHOLD; - - DefaultResourceSend(*mServer, response, ifs); - } - else - { - throw std::invalid_argument("could not read file"); - } - - } catch (const std::exception &e) - { - std::string content = "Could not open path `" + request->path + "`: " + e.what(); - EscapeHtml(content); - *response << OT_RESPONSE_FAILURE_STATUS << OT_RESPONSE_HEADER_LENGTH << content.length() - << OT_RESPONSE_PLACEHOLD << content; - } - }; -} - -std::string WebServer::HandleJoinNetworkRequest(const std::string &aJoinRequest, void *aUserData) -{ - WebServer *webServer = static_cast(aUserData); - - return webServer->HandleJoinNetworkRequest(aJoinRequest); -} - -std::string WebServer::HandleFormNetworkRequest(const std::string &aFormRequest, void *aUserData) -{ - WebServer *webServer = static_cast(aUserData); - - return webServer->HandleFormNetworkRequest(aFormRequest); -} - -std::string WebServer::HandleAddPrefixRequest(const std::string &aAddPrefixRequest, void *aUserData) -{ - WebServer *webServer = static_cast(aUserData); - - return webServer->HandleAddPrefixRequest(aAddPrefixRequest); -} - -std::string WebServer::HandleDeletePrefixRequest(const std::string &aDeletePrefixRequest, void *aUserData) -{ - WebServer *webServer = static_cast(aUserData); - - return webServer->HandleDeletePrefixRequest(aDeletePrefixRequest); -} - -std::string WebServer::HandleGetStatusRequest(const std::string &aGetStatusRequest, void *aUserData) -{ - WebServer *webServer = static_cast(aUserData); - - return webServer->HandleGetStatusRequest(aGetStatusRequest); -} - -std::string WebServer::HandleGetAvailableNetworkResponse(const std::string &aGetAvailableNetworkRequest, - void * aUserData) -{ - WebServer *webServer = static_cast(aUserData); - - return webServer->HandleGetAvailableNetworkResponse(aGetAvailableNetworkRequest); -} - -std::string WebServer::HandleCommission(const std::string &aCommissionRequest, void *aUserData) -{ - WebServer *webServer = static_cast(aUserData); - - return webServer->HandleCommission(aCommissionRequest); -} - -void WebServer::ResponseJoinNetwork(void) -{ - HandleHttpRequest(OT_JOIN_NETWORK_PATH, OT_REQUEST_METHOD_POST, HandleJoinNetworkRequest); -} - -void WebServer::ResponseFormNetwork(void) -{ - HandleHttpRequest(OT_FORM_NETWORK_PATH, OT_REQUEST_METHOD_POST, HandleFormNetworkRequest); -} - -void WebServer::ResponseAddOnMeshPrefix(void) -{ - HandleHttpRequest(OT_ADD_PREFIX_PATH, OT_REQUEST_METHOD_POST, HandleAddPrefixRequest); -} - -void WebServer::ResponseDeleteOnMeshPrefix(void) -{ - HandleHttpRequest(OT_DELETE_PREFIX_PATH, OT_REQUEST_METHOD_POST, HandleDeletePrefixRequest); -} - -void WebServer::ResponseGetStatus(void) -{ - HandleHttpRequest(OT_GET_NETWORK_PATH, OT_REQUEST_METHOD_GET, HandleGetStatusRequest); -} - -void WebServer::ResponseGetAvailableNetwork(void) -{ - HandleHttpRequest(OT_AVAILABLE_NETWORK_PATH, OT_REQUEST_METHOD_GET, HandleGetAvailableNetworkResponse); -} - -void WebServer::ResponseCommission(void) -{ - HandleHttpRequest(OT_COMMISSIONER_START_PATH, OT_REQUEST_METHOD_POST, HandleCommission); -} - -std::string WebServer::HandleJoinNetworkRequest(const std::string &aJoinRequest) -{ - return mWpanService.HandleJoinNetworkRequest(aJoinRequest); -} - -std::string WebServer::HandleFormNetworkRequest(const std::string &aFormRequest) -{ - return mWpanService.HandleFormNetworkRequest(aFormRequest); -} - -std::string WebServer::HandleAddPrefixRequest(const std::string &aAddPrefixRequest) -{ - return mWpanService.HandleAddPrefixRequest(aAddPrefixRequest); -} - -std::string WebServer::HandleDeletePrefixRequest(const std::string &aDeletePrefixRequest) -{ - return mWpanService.HandleDeletePrefixRequest(aDeletePrefixRequest); -} - -std::string WebServer::HandleGetStatusRequest(const std::string &aGetStatusRequest) -{ - (void)aGetStatusRequest; - return mWpanService.HandleStatusRequest(); -} - -std::string WebServer::HandleGetAvailableNetworkResponse(const std::string &aGetAvailableNetworkRequest) -{ - (void)aGetAvailableNetworkRequest; - return mWpanService.HandleAvailableNetworkRequest(); -} - -std::string WebServer::HandleCommission(const std::string &aCommissionRequest) -{ - return mWpanService.HandleCommission(aCommissionRequest); -} - -} // namespace Web -} // namespace otbr diff --git a/src/web/web-service/web_server.hpp b/src/web/web-service/web_server.hpp deleted file mode 100644 index 9e9b95768f1..00000000000 --- a/src/web/web-service/web_server.hpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2017, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * This file implements the web server of border router - */ - -#ifndef OTBR_WEB_WEB_SERVICE_WEB_SERVER_ -#define OTBR_WEB_WEB_SERVICE_WEB_SERVER_ - -#include "openthread-br/config.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "web/web-service/wpan_service.hpp" - -namespace SimpleWeb { -template class Server; -typedef boost::asio::ip::tcp::socket HTTP; -} // namespace SimpleWeb - -namespace otbr { -namespace Web { - -typedef SimpleWeb::Server HttpServer; - -/** - * This class implements the http server. - * - */ -class WebServer -{ -public: - /** - * This method is constructor to initialize the WebServer. - * - */ - WebServer(void); - - /** - * This method is destructor to free the WebServer. - * - */ - ~WebServer(void); - - /** - * This method starts the Web Server. - * - * @param[in] aIfName The pointer to the Thread interface name. - * @param[in] aListenAddr The http server listen address, can be nullptr for any address. - * @param[in] aPort The port of http server. - * - */ - void StartWebServer(const char *aIfName, const char *aListenAddr, uint16_t aPort); - - /** - * This method stops the Web Server. - * - */ - void StopWebServer(void); - -private: - typedef std::string (*HttpRequestCallback)(const std::string &aRequest, void *aUserData); - static std::string HandleJoinNetworkRequest(const std::string &aJoinRequest, void *aUserData); - static std::string HandleFormNetworkRequest(const std::string &aFormRequest, void *aUserData); - static std::string HandleAddPrefixRequest(const std::string &aAddPrefixRequest, void *aUserData); - static std::string HandleDeletePrefixRequest(const std::string &aDeletePrefixRequest, void *aUserData); - static std::string HandleGetStatusRequest(const std::string &aGetStatusRequest, void *aUserData); - static std::string HandleGetAvailableNetworkResponse(const std::string &aGetAvailableNetworkRequest, - void * aUserData); - static std::string HandleCommission(const std::string &aCommissionRequest, void *aUserData); - - std::string HandleJoinNetworkRequest(const std::string &aJoinRequest); - std::string HandleFormNetworkRequest(const std::string &aFormRequest); - std::string HandleAddPrefixRequest(const std::string &aAddPrefixRequest); - std::string HandleDeletePrefixRequest(const std::string &aDeletePrefixRequest); - std::string HandleGetStatusRequest(const std::string &aGetStatusRequest); - std::string HandleGetAvailableNetworkResponse(const std::string &aGetAvailableNetworkRequest); - std::string HandleCommission(const std::string &aCommissionRequest); - - void HandleHttpRequest(const char *aUrl, const char *aMethod, HttpRequestCallback aCallback); - void ResponseJoinNetwork(void); - void ResponseFormNetwork(void); - void ResponseAddOnMeshPrefix(void); - void ResponseDeleteOnMeshPrefix(void); - void ResponseGetStatus(void); - void ResponseGetAvailableNetwork(void); - void DefaultHttpResponse(void); - void ResponseCommission(void); - - void Init(void); - - HttpServer * mServer; - otbr::Web::WpanService mWpanService; -}; - -} // namespace Web -} // namespace otbr - -#endif // OTBR_WEB_WEB_SERVICE_WEB_SERVER_ diff --git a/src/web/web-service/wpan_service.cpp b/src/web/web-service/wpan_service.cpp deleted file mode 100644 index 0f0b1d561ce..00000000000 --- a/src/web/web-service/wpan_service.cpp +++ /dev/null @@ -1,486 +0,0 @@ -/* - * Copyright (c) 2017, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * This file implements the wpan controller service - */ - -#include "web/web-service/wpan_service.hpp" - -#include -#include -#include - -#include "common/byteswap.hpp" -#include "common/code_utils.hpp" - -namespace otbr { -namespace Web { - -const char *WpanService::kBorderAgentHost = "127.0.0.1"; -const char *WpanService::kBorderAgentPort = "49191"; - -#define WPAN_RESPONSE_SUCCESS "successful" -#define WPAN_RESPONSE_FAILURE "failed" - -std::string WpanService::HandleJoinNetworkRequest(const std::string &aJoinRequest) -{ - Json::Value root; - Json::Reader reader; - Json::FastWriter jsonWriter; - std::string response; - int index; - std::string masterKey; - std::string prefix; - bool defaultRoute; - int ret = kWpanStatus_Ok; - otbr::Web::OpenThreadClient client; - - VerifyOrExit(client.Connect(), ret = kWpanStatus_SetFailed); - - VerifyOrExit(reader.parse(aJoinRequest.c_str(), root) == true, ret = kWpanStatus_ParseRequestFailed); - index = root["index"].asUInt(); - masterKey = root["masterKey"].asString(); - prefix = root["prefix"].asString(); - defaultRoute = root["defaultRoute"].asBool(); - - if (prefix.find('/') == std::string::npos) - { - prefix += "/64"; - } - - VerifyOrExit(client.FactoryReset(), ret = kWpanStatus_LeaveFailed); - VerifyOrExit((ret = commitActiveDataset(client, masterKey, mNetworks[index].mNetworkName, mNetworks[index].mChannel, - mNetworks[index].mExtPanId, mNetworks[index].mPanId)) == kWpanStatus_Ok); - VerifyOrExit(client.Execute("ifconfig up") != nullptr, ret = kWpanStatus_JoinFailed); - VerifyOrExit(client.Execute("thread start") != nullptr, ret = kWpanStatus_JoinFailed); - VerifyOrExit(client.Execute("prefix add %s paso%s", prefix.c_str(), (defaultRoute ? "r" : "")) != nullptr, - ret = kWpanStatus_SetFailed); -exit: - - root.clear(); - root["result"] = WPAN_RESPONSE_SUCCESS; - - root["error"] = ret; - if (ret != kWpanStatus_Ok) - { - otbrLog(OTBR_LOG_ERR, "wpan service error: %d", ret); - root["result"] = WPAN_RESPONSE_FAILURE; - } - response = jsonWriter.write(root); - return response; -} - -std::string WpanService::HandleFormNetworkRequest(const std::string &aFormRequest) -{ - Json::Value root; - Json::FastWriter jsonWriter; - Json::Reader reader; - std::string response; - otbr::Psk::Pskc psk; - char pskcStr[OT_PSKC_MAX_LENGTH * 2 + 1]; - uint8_t extPanIdBytes[OT_EXTENDED_PANID_LENGTH]; - std::string masterKey; - std::string prefix; - uint16_t channel; - std::string networkName; - std::string passphrase; - uint16_t panId; - uint64_t extPanId; - bool defaultRoute; - int ret = kWpanStatus_Ok; - otbr::Web::OpenThreadClient client; - - VerifyOrExit(client.Connect(), ret = kWpanStatus_SetFailed); - - pskcStr[OT_PSKC_MAX_LENGTH * 2] = '\0'; // for manipulating with strlen - VerifyOrExit(reader.parse(aFormRequest.c_str(), root) == true, ret = kWpanStatus_ParseRequestFailed); - masterKey = root["masterKey"].asString(); - prefix = root["prefix"].asString(); - channel = root["channel"].asUInt(); - networkName = root["networkName"].asString(); - passphrase = root["passphrase"].asString(); - VerifyOrExit(sscanf(root["panId"].asString().c_str(), "%hx", &panId) == 1, ret = kWpanStatus_ParseRequestFailed); - VerifyOrExit(sscanf(root["extPanId"].asString().c_str(), "%" PRIx64, &extPanId) == 1, - ret = kWpanStatus_ParseRequestFailed); - defaultRoute = root["defaultRoute"].asBool(); - - otbr::Utils::Hex2Bytes(root["extPanId"].asString().c_str(), extPanIdBytes, OT_EXTENDED_PANID_LENGTH); - otbr::Utils::Bytes2Hex(psk.ComputePskc(extPanIdBytes, networkName.c_str(), passphrase.c_str()), OT_PSKC_MAX_LENGTH, - pskcStr); - - if (prefix.find('/') == std::string::npos) - { - prefix += "/64"; - } - - VerifyOrExit(client.FactoryReset(), ret = kWpanStatus_LeaveFailed); - VerifyOrExit((ret = commitActiveDataset(client, masterKey, networkName, channel, extPanId, panId)) == - kWpanStatus_Ok); - VerifyOrExit(client.Execute("pskc %s", pskcStr) != nullptr, ret = kWpanStatus_SetFailed); - VerifyOrExit(client.Execute("ifconfig up") != nullptr, ret = kWpanStatus_FormFailed); - VerifyOrExit(client.Execute("thread start") != nullptr, ret = kWpanStatus_FormFailed); - VerifyOrExit(client.Execute("prefix add %s paso%s", prefix.c_str(), (defaultRoute ? "r" : "")) != nullptr, - ret = kWpanStatus_SetFailed); -exit: - - root.clear(); - - root["result"] = WPAN_RESPONSE_SUCCESS; - root["error"] = ret; - if (ret != kWpanStatus_Ok) - { - otbrLog(OTBR_LOG_ERR, "wpan service error: %d", ret); - root["result"] = WPAN_RESPONSE_FAILURE; - } - response = jsonWriter.write(root); - return response; -} - -std::string WpanService::HandleAddPrefixRequest(const std::string &aAddPrefixRequest) -{ - Json::Value root; - Json::FastWriter jsonWriter; - Json::Reader reader; - std::string response; - std::string prefix; - bool defaultRoute; - int ret = kWpanStatus_Ok; - otbr::Web::OpenThreadClient client; - - VerifyOrExit(client.Connect(), ret = kWpanStatus_SetFailed); - - VerifyOrExit(reader.parse(aAddPrefixRequest.c_str(), root) == true, ret = kWpanStatus_ParseRequestFailed); - prefix = root["prefix"].asString(); - defaultRoute = root["defaultRoute"].asBool(); - - VerifyOrExit(client.Execute("prefix add %s paso%s", prefix.c_str(), (defaultRoute ? "r" : "")) != nullptr, - ret = kWpanStatus_SetGatewayFailed); -exit: - - root.clear(); - - root["result"] = WPAN_RESPONSE_SUCCESS; - root["error"] = ret; - if (ret != kWpanStatus_Ok) - { - otbrLog(OTBR_LOG_ERR, "wpan service error: %d", ret); - root["result"] = WPAN_RESPONSE_FAILURE; - } - response = jsonWriter.write(root); - return response; -} - -std::string WpanService::HandleDeletePrefixRequest(const std::string &aDeleteRequest) -{ - Json::Value root; - Json::FastWriter jsonWriter; - Json::Reader reader; - std::string response; - std::string prefix; - int ret = kWpanStatus_Ok; - otbr::Web::OpenThreadClient client; - - VerifyOrExit(client.Connect(), ret = kWpanStatus_SetFailed); - - VerifyOrExit(reader.parse(aDeleteRequest.c_str(), root) == true, ret = kWpanStatus_ParseRequestFailed); - prefix = root["prefix"].asString(); - - VerifyOrExit(client.Execute("prefix remove %s", prefix.c_str()) != nullptr, ret = kWpanStatus_SetGatewayFailed); -exit: - - root.clear(); - root["result"] = WPAN_RESPONSE_SUCCESS; - - root["error"] = ret; - if (ret != kWpanStatus_Ok) - { - otbrLog(OTBR_LOG_ERR, "wpan service error: %d", ret); - root["result"] = WPAN_RESPONSE_FAILURE; - } - response = jsonWriter.write(root); - return response; -} - -std::string WpanService::HandleStatusRequest() -{ - Json::Value root, networkInfo; - Json::FastWriter jsonWriter; - std::string response, networkName, extPanId, propertyValue; - int ret = kWpanStatus_Ok; - otbr::Web::OpenThreadClient client; - char * rval; - - networkInfo["WPAN service"] = "uninitialized"; - VerifyOrExit(client.Connect(), ret = kWpanStatus_SetFailed); - - VerifyOrExit((rval = client.Execute("state")) != nullptr, ret = kWpanStatus_GetPropertyFailed); - networkInfo["NCP:State"] = rval; - - if (!strcmp(rval, "disabled")) - { - networkInfo["WPAN service"] = "offline"; - ExitNow(); - } - else if (!strcmp(rval, "detached")) - { - networkInfo["WPAN service"] = "associating"; - ExitNow(); - } - else - { - networkInfo["WPAN service"] = "associated"; - } - - VerifyOrExit((rval = client.Execute("version")) != nullptr, ret = kWpanStatus_GetPropertyFailed); - networkInfo["NCP:Version"] = rval; - - VerifyOrExit((rval = client.Execute("eui64")) != nullptr, ret = kWpanStatus_GetPropertyFailed); - networkInfo["NCP:HardwareAddress"] = rval; - - VerifyOrExit((rval = client.Execute("channel")) != nullptr, ret = kWpanStatus_GetPropertyFailed); - networkInfo["NCP:Channel"] = rval; - - VerifyOrExit((rval = client.Execute("state")) != nullptr, ret = kWpanStatus_GetPropertyFailed); - networkInfo["Network:NodeType"] = rval; - - VerifyOrExit((rval = client.Execute("networkname")) != nullptr, ret = kWpanStatus_GetPropertyFailed); - networkInfo["Network:Name"] = rval; - - VerifyOrExit((rval = client.Execute("extpanid")) != nullptr, ret = kWpanStatus_GetPropertyFailed); - networkInfo["Network:XPANID"] = rval; - - VerifyOrExit((rval = client.Execute("panid")) != nullptr, ret = kWpanStatus_GetPropertyFailed); - networkInfo["Network:PANID"] = rval; - - { - static const char kMeshLocalPrefixLocator[] = "Mesh Local Prefix: "; - static const char kMeshLocalAddressTokenLocator[] = "0:ff:fe00:"; - std::string meshLocalPrefix; - - VerifyOrExit((rval = client.Execute("dataset active")) != nullptr, ret = kWpanStatus_GetPropertyFailed); - rval = strstr(rval, kMeshLocalPrefixLocator); - rval += sizeof(kMeshLocalPrefixLocator) - 1; - *strstr(rval, "\r\n") = '\0'; - - networkInfo["IPv6:MeshLocalPrefix"] = rval; - - meshLocalPrefix = rval; - meshLocalPrefix.resize(meshLocalPrefix.find('/')); - - VerifyOrExit((rval = client.Execute("ipaddr")) != nullptr, ret = kWpanStatus_GetPropertyFailed); - - for (rval = strtok(rval, "\r\n"); rval != nullptr; rval = strtok(nullptr, "\r\n")) - { - char *meshLocalAddressToken = nullptr; - - if (strstr(rval, meshLocalPrefix.c_str()) != rval) - { - continue; - } - - meshLocalAddressToken = strstr(rval, kMeshLocalAddressTokenLocator); - - if (meshLocalAddressToken == nullptr) - { - break; - } - - // In case this address is not ends with 0:ff:fe00:xxxx - if (strchr(meshLocalAddressToken + sizeof(kMeshLocalAddressTokenLocator) - 1, ':') != nullptr) - { - break; - } - } - - networkInfo["IPv6:MeshLocalAddress"] = rval; - } - -exit: - root["result"] = networkInfo; - - if (ret != kWpanStatus_Ok) - { - root["result"] = WPAN_RESPONSE_FAILURE; - otbrLog(OTBR_LOG_ERR, "wpan service error: %d", ret); - } - root["error"] = ret; - response = jsonWriter.write(root); - return response; -} - -std::string WpanService::HandleAvailableNetworkRequest() -{ - Json::Value root, networks, networkInfo; - Json::FastWriter jsonWriter; - std::string response; - int ret = kWpanStatus_Ok; - otbr::Web::OpenThreadClient client; - - VerifyOrExit(client.Connect(), ret = kWpanStatus_ScanFailed); - VerifyOrExit((mNetworksCount = client.Scan(mNetworks, sizeof(mNetworks) / sizeof(mNetworks[0]))) > 0, - ret = kWpanStatus_NetworkNotFound); - - for (int i = 0; i < mNetworksCount; i++) - { - char extPanId[OT_EXTENDED_PANID_LENGTH * 2 + 1], panId[OT_PANID_LENGTH * 2 + 3], - hardwareAddress[OT_HARDWARE_ADDRESS_LENGTH * 2 + 1]; - otbr::Utils::Long2Hex(bswap_64(mNetworks[i].mExtPanId), extPanId); - otbr::Utils::Bytes2Hex(mNetworks[i].mHardwareAddress, OT_HARDWARE_ADDRESS_LENGTH, hardwareAddress); - sprintf(panId, "0x%X", mNetworks[i].mPanId); - networkInfo[i]["nn"] = mNetworks[i].mNetworkName; - networkInfo[i]["xp"] = extPanId; - networkInfo[i]["pi"] = panId; - networkInfo[i]["ch"] = mNetworks[i].mChannel; - networkInfo[i]["ha"] = hardwareAddress; - } - - root["result"] = networkInfo; - -exit: - if (ret != kWpanStatus_Ok) - { - root["result"] = WPAN_RESPONSE_FAILURE; - otbrLog(OTBR_LOG_ERR, "Error is %d", ret); - } - root["error"] = ret; - response = jsonWriter.write(root); - return response; -} - -int WpanService::GetWpanServiceStatus(std::string &aNetworkName, std::string &aExtPanId) const -{ - int status = kWpanStatus_Ok; - otbr::Web::OpenThreadClient client; - const char * rval; - - VerifyOrExit(client.Connect(), status = kWpanStatus_Uninitialized); - rval = client.Execute("state"); - VerifyOrExit(rval != nullptr, status = kWpanStatus_Down); - if (!strcmp(rval, "disabled")) - { - status = kWpanStatus_Offline; - } - else if (!strcmp(rval, "detached")) - { - status = kWpanStatus_Associating; - } - else - { - rval = client.Execute("networkname"); - VerifyOrExit(rval != nullptr, status = kWpanStatus_Down); - aNetworkName = rval; - - rval = client.Execute("extpanid"); - VerifyOrExit(rval != nullptr, status = kWpanStatus_Down); - aExtPanId = rval; - } - -exit: - - return status; -} - -std::string WpanService::HandleCommission(const std::string &aCommissionRequest) -{ - Json::Value root; - Json::Reader reader; - int ret = kWpanStatus_Ok; - std::string pskd; - std::string response; - - VerifyOrExit(reader.parse(aCommissionRequest.c_str(), root) == true, ret = kWpanStatus_ParseRequestFailed); - pskd = root["pskd"].asString(); - { - otbr::Web::OpenThreadClient client; - const char * rval; - - VerifyOrExit(client.Connect(), ret = kWpanStatus_Uninitialized); - rval = client.Execute("commissioner start"); - VerifyOrExit(rval != nullptr, ret = kWpanStatus_Down); - rval = client.Execute("commissioner joiner add * %s", pskd.c_str()); - VerifyOrExit(rval != nullptr, ret = kWpanStatus_Down); - root["error"] = ret; - } -exit: - if (ret != kWpanStatus_Ok) - { - root["result"] = WPAN_RESPONSE_FAILURE; - otbrLog(OTBR_LOG_ERR, "error: %d", ret); - } - return response; -} - -int WpanService::commitActiveDataset(otbr::Web::OpenThreadClient &aClient, - const std::string & aMasterKey, - const std::string & aNetworkName, - uint16_t aChannel, - uint64_t aExtPanId, - uint16_t aPanId) -{ - int ret = kWpanStatus_Ok; - - VerifyOrExit(aClient.Execute("dataset init new") != nullptr, ret = kWpanStatus_SetFailed); - VerifyOrExit(aClient.Execute("dataset masterkey %s", aMasterKey.c_str()) != nullptr, ret = kWpanStatus_SetFailed); - VerifyOrExit(aClient.Execute("dataset networkname %s", escapeOtCliEscapable(aNetworkName).c_str()) != nullptr, - ret = kWpanStatus_SetFailed); - VerifyOrExit(aClient.Execute("dataset channel %u", aChannel) != nullptr, ret = kWpanStatus_SetFailed); - VerifyOrExit(aClient.Execute("dataset extpanid %016" PRIx64, aExtPanId) != nullptr, ret = kWpanStatus_SetFailed); - VerifyOrExit(aClient.Execute("dataset panid %u", aPanId) != nullptr, ret = kWpanStatus_SetFailed); - VerifyOrExit(aClient.Execute("dataset commit active") != nullptr, ret = kWpanStatus_SetFailed); - -exit: - return ret; -} - -std::string WpanService::escapeOtCliEscapable(const std::string &aArg) -{ - std::stringbuf strbuf; - - for (char c : aArg) - { - switch (c) - { - case ' ': - case '\t': - case '\r': - case '\n': - case '\\': - strbuf.sputc('\\'); - // Fallthrough - default: - strbuf.sputc(c); - } - } - - return strbuf.str(); -} - -} // namespace Web -} // namespace otbr diff --git a/src/web/web-service/wpan_service.hpp b/src/web/web-service/wpan_service.hpp deleted file mode 100644 index 54a86a74a57..00000000000 --- a/src/web/web-service/wpan_service.hpp +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (c) 2017, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * This file implements the wpan controller service - */ - -#ifndef OTBR_WEB_WEB_SERVICE_WPAN_SERVICE_ -#define OTBR_WEB_WEB_SERVICE_WPAN_SERVICE_ - -#include "openthread-br/config.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include "common/logging.hpp" -#include "utils/hex.hpp" -#include "utils/pskc.hpp" -#include "web/web-service/ot_client.hpp" - -/** - * WPAN parameter constants - * - */ - -#define OT_EXTENDED_PANID_LENGTH 8 -#define OT_HARDWARE_ADDRESS_LENGTH 8 -#define OT_NETWORK_NAME_LENGTH 16 -#define OT_PANID_LENGTH 2 -#define OT_PSKC_MAX_LENGTH 16 -#define OT_HEX_PREFIX_LENGTH 2 -#define OT_PUBLISH_SERVICE_INTERVAL 20 - -namespace otbr { -namespace Web { - -/** - * This class provides web service to manage WPAN. - * - */ -class WpanService -{ -public: - /** - * This method handles the http request to join network. - * - * @param[in] aJoinRequest A reference to the http request of joining network. - * - * @returns The string to the http response of joining network. - * - */ - std::string HandleJoinNetworkRequest(const std::string &aJoinRequest); - - /** - * This method handles the http request to form network. - * - * @param[in] aFormRequest A reference to the http request of forming network. - * - * @returns The string to the http response of forming network. - * - */ - std::string HandleFormNetworkRequest(const std::string &aFormRequest); - - /** - * This method handles the http request to add on-mesh prefix. - * - * @param[in] aAddPrefixRequest A reference to the http request of adding on-mesh prefix. - * - * @returns The string to the http response of adding on-mesh prefix. - * - */ - std::string HandleAddPrefixRequest(const std::string &aAddPrefixRequest); - - /** - * This method handles the http request to delete on-mesh prefix http request. - * - * @param[in] aDeleteRequest A reference to the http request of deleting on-mesh prefix. - * - * @returns The string to the http response of deleting on-mesh prefix. - * - */ - std::string HandleDeletePrefixRequest(const std::string &aDeleteRequest); - - /** - * This method handles http request to get netowrk status. - * - * @returns The string to the http response of getting status. - * - */ - std::string HandleStatusRequest(void); - - /** - * This method handles http request to get available networks. - * - * @returns The string to the http response of getting available networks. - * - */ - std::string HandleAvailableNetworkRequest(void); - - /** - * This method handles http request to commission device - * - * @returns The string to the http response of commissioning - * - */ - std::string HandleCommission(const std::string &aCommissionRequest); - - /** - * This method sets the Thread interface name. - * - * @param[in] aIfName The pointer to the Thread interface name. - * - */ - void SetInterfaceName(const char *aIfName) - { - strncpy(mIfName, aIfName, sizeof(mIfName) - 1); - mIfName[sizeof(mIfName) - 1] = '\0'; - } - - /** - * This method gets status of wpan service. - * - * @param[inout] aNetworkName The pointer to the network name. - * @param[inout] aIfName The pointer to the extended PAN ID. - * - * @retval kWpanStatus_OK Successfully started the Thread service. - * @retval kWpanStatus_Offline Not started the Thread service. - * @retval kWpanStatus_Down The Thread service was down. - * - */ - int GetWpanServiceStatus(std::string &aNetworkName, std::string &aExtPanId) const; - - /** - * This method starts commissioner and wait for a device to join - * - * @param[in] aPskd Joiner pskd - * @param[in] aNetworkPassword Network password - * - * @returns The string to the http response of getting available networks. - * - */ - std::string CommissionDevice(const char *aPskd, const char *aNetworkPassword); - -private: - int commitActiveDataset(otbr::Web::OpenThreadClient &aClient, - const std::string & aMasterKey, - const std::string & aNetworkName, - uint16_t aChannel, - uint64_t aExtPanId, - uint16_t aPanId); - static std::string escapeOtCliEscapable(const std::string &aArg); - - WpanNetworkInfo mNetworks[OT_SCANNED_NET_BUFFER_SIZE]; - int mNetworksCount; - char mIfName[IFNAMSIZ]; - std::string mNetworkName; - std::string mExtPanId; - - enum - { - kWpanStatus_Ok = 0, - kWpanStatus_Associating, - kWpanStatus_Down, - kWpanStatus_FormFailed, - kWpanStatus_GetPropertyFailed, - kWpanStatus_JoinFailed, - kWpanStatus_LeaveFailed, - kWpanStatus_NetworkNotFound, - kWpanStatus_Offline, - kWpanStatus_ParseRequestFailed, - kWpanStatus_ScanFailed, - kWpanStatus_SetFailed, - kWpanStatus_SetGatewayFailed, - kWpanStatus_Uninitialized, - }; - - enum - { - kPropertyType_String = 0, - kPropertyType_Data, - }; - - static const char *kBorderAgentHost; - static const char *kBorderAgentPort; -}; - -} // namespace Web -} // namespace otbr - -#endif // OTBR_WEB_WEB_SERVICE_WPAN_SERVICE_ diff --git a/tests/rest/test-rest-server b/tests/rest/test-rest-server index 159c2348d50..3a3d97c168d 100755 --- a/tests/rest/test-rest-server +++ b/tests/rest/test-rest-server @@ -48,6 +48,43 @@ main() sudo "${CMAKE_BINARY_DIR}"/src/agent/otbr-agent -d 7 -v -I wpan0 "spinel+hdlc+forkpty://$(command -v ot-rcp)?forkpty-arg=1" & trap on_exit EXIT sleep 5 + expect < 1 + + print("join network pass") + + def main(): - node_test(200) - node_rloc_test(200) - node_rloc16_test(200) - node_ext_address_test(200) - node_state_test(200) - node_network_name_test(200) - node_leader_data_test(200) - node_num_of_router_test(200) - node_ext_panid_test(200) - diagnostics_test(20) + join() + form_network_test() + scan() + add_remove_prefix() + commissioner() + node_test(100) + node_rloc_test(100) + node_rloc16_test(100) + node_ext_address_test(100) + node_state_test(100) + node_network_name_test(100) + node_leader_data_test(100) + node_num_of_router_test(100) + node_ext_panid_test(100) + diagnostics_test(10) error_test(10) return 0 diff --git a/tests/scripts/check-scan-build b/tests/scripts/check-scan-build index 0e1c12c19e3..ce4269f3145 100755 --- a/tests/scripts/check-scan-build +++ b/tests/scripts/check-scan-build @@ -39,7 +39,6 @@ main() -DCMAKE_CXX_COMPILER=clang++ \ -DCMAKE_C_COMPILER=clang \ -DOTBR_DBUS=ON \ - -DOTBR_WEB=ON \ -DOTBR_REST=ON \ .. \ && scan-build -o "${SCAN_TMPDIR}" -analyze-headers -v ninja) diff --git a/tests/scripts/check-scripts b/tests/scripts/check-scripts index 6ac1d957b51..5b390973f92 100755 --- a/tests/scripts/check-scripts +++ b/tests/scripts/check-scripts @@ -75,7 +75,6 @@ main() netstat -an | grep 80 pidof tayga kill "${SERVICES_PID}" - sudo killall otbr-web || true sudo killall otbr-agent || true sudo service tayga stop || true killall ot-rcp diff --git a/tests/scripts/meshcop b/tests/scripts/meshcop index f083f237aa6..e57ff0d0790 100755 --- a/tests/scripts/meshcop +++ b/tests/scripts/meshcop @@ -54,7 +54,6 @@ readonly OTBR_USE_WEB_COMMISSIONER="${USE_WEB_COMMISSIONER:-0}" readonly TEST_BASE=/tmp/test-otbr readonly OTBR_AGENT=otbr-agent -readonly OTBR_WEB=otbr-web readonly OT_COMMISSIONER_CLI=commissioner-cli readonly STAGE_DIR="${TEST_BASE}/stage" @@ -62,16 +61,15 @@ readonly BUILD_DIR="${TEST_BASE}/build" readonly OTBR_PSKC_PATH="${ABS_TOP_BUILDDIR}/tools/pskc" readonly OTBR_AGENT_PATH="${ABS_TOP_BUILDDIR}/src/agent/${OTBR_AGENT}" readonly OTBR_DBUS_CONF="${ABS_TOP_BUILDDIR}/src/agent/otbr-agent.conf" -readonly OTBR_WEB_PATH="${ABS_TOP_BUILDDIR}/src/web/${OTBR_WEB}" # The node ids readonly LEADER_NODE_ID=1 readonly JOINER_NODE_ID=2 -# Web GUI -readonly OTBR_WEB_HOST=127.0.0.1 -readonly OTBR_WEB_PORT=8773 -readonly OTBR_WEB_URL="http://${OTBR_WEB_HOST}:${OTBR_WEB_PORT}" +# HTTP REST Server +readonly OTBR_REST_HOST=127.0.0.1 +readonly OTBR_REST_PORT=8081 +readonly OTBR_REST_URL="http://${OTBR_REST_HOST}:${OTBR_REST_PORT}" # # NOTE Joiner pass phrase: @@ -207,7 +205,6 @@ test_setup() exit_message="JOINER FAILED" executable_or_die "${OTBR_AGENT_PATH}" - executable_or_die "${OTBR_WEB_PATH}" # Remove flashes sudo rm -vrf "${TEST_BASE}/tmp" @@ -234,7 +231,6 @@ test_teardown() write_syslog "EXIT ${EXIT_CODE} - output logs" sudo pkill -f "${OTBR_AGENT}" || true - sudo pkill -f "${OTBR_WEB}" || true sudo pkill -f "${OT_COMMISSIONER_CLI}" || true sudo pkill -f "${OT_CLI}" || true wait @@ -284,24 +280,6 @@ ba_start() write_syslog "AGENT: start complete" } -web_start() -{ - write_syslog "WEB: kill old" - sudo killall "${OTBR_WEB}" || true - write_syslog "WEB: starting" - ( - set -e - set -x - - cd "${ORIGIN_PWD}" - sudo "${OTBR_WEB_PATH}" -I "${TUN_NAME}" -p "${OTBR_WEB_PORT}" -a "${OTBR_WEB_HOST}" & - ) - sleep 15 - - pidof ${OTBR_WEB} || die "WEB: failed to start" - write_syslog "WEB: start complete" -} - network_form() { readonly OT_PANID="$(random_panid)" @@ -309,7 +287,7 @@ network_form() readonly OT_MASTER_KEY="$(random_masterkey)" readonly OT_CHANNEL="$(random_channel)" - curl --header "Content-Type: application/json" --request POST --data "{\"masterKey\":\"${OT_MASTER_KEY}\",\"prefix\":\"fd11:22::\",\"defaultRoute\":true,\"extPanId\":\"${OT_XPANID}\",\"panId\":\"${OT_PANID}\",\"passphrase\":\"${OT_AGENT_PASSPHRASE}\",\"channel\":${OT_CHANNEL},\"networkName\":\"${OT_NETWORK_NAME}\"}" "${OTBR_WEB_URL}"/form_network | grep "success" || die "WEB: form failed" + curl --header "Content-Type: application/json" --request POST --data "{\"masterKey\":\"${OT_MASTER_KEY}\",\"prefix\":\"fd11:22::\",\"defaultRoute\":true,\"extPanId\":\"${OT_XPANID}\",\"panId\":\"${OT_PANID}\",\"passphrase\":\"${OT_AGENT_PASSPHRASE}\",\"channel\":${OT_CHANNEL},\"networkName\":\"${OT_NETWORK_NAME}\"}" "${OTBR_REST_URL}"/v1/networks | grep "${OT_NETWORK_NAME}" || die "WEB: form failed" sleep 15 # verify mDNS is working as expected. local mdns_result="${TEST_BASE}"/mdns_result.log @@ -371,7 +349,7 @@ EOF web_commissioner_start() { - curl --header "Content-Type: application/json" --request POST --data "{\"pskd\":\"${OT_JOINER_PASSPHRASE}\", \"passphrase\":\"${OT_AGENT_PASSPHRASE}\"}" "${OTBR_WEB_URL}"/commission + curl --header "Content-Type: application/json" --request POST --data "{\"pskd\":\"${OT_JOINER_PASSPHRASE}\", \"passphrase\":\"${OT_AGENT_PASSPHRASE}\"}" "${OTBR_REST_URL}"/v1/networks/current/commission sleep 15 } @@ -403,7 +381,6 @@ main() { test_setup ba_start - web_start network_form if [[ ${OTBR_USE_WEB_COMMISSIONER} == 1 ]]; then web_commissioner_start diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index 8e8aaf77a23..28b0cce02dd 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -27,7 +27,12 @@ # add_subdirectory(openthread) + if(OTBR_REST) - add_subdirectory(cJSON) - add_subdirectory(http-parser) + add_subdirectory(angular) + add_subdirectory(angular-material) + add_subdirectory(d3js) + add_subdirectory(cJSON EXCLUDE_FROM_ALL) + add_subdirectory(http-parser EXCLUDE_FROM_ALL) + add_subdirectory(mdl) endif() diff --git a/third_party/Simple-web-server/README.md b/third_party/Simple-web-server/README.md deleted file mode 100644 index 6cd7b1ec07c..00000000000 --- a/third_party/Simple-web-server/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Simple-web-server - -## URL - -https://github.com/eidheim/Simple-Web-Server - -## Version - -none - -## Commit - -cfafbcbc7d74d91ffd24fd2fbf1a382d52133264 - -## License - -MIT License - -## License File - -[LICENSE](repo/LICENSE.txt) - -## Description - -A very simple, fast, multithreaded, platform independent HTTP and HTTPS server and client library implemented using C++11 and Boost.Asio. Created to be an easy way to make REST resources available from C++ applications. diff --git a/third_party/Simple-web-server/repo/CMakeLists.txt b/third_party/Simple-web-server/repo/CMakeLists.txt deleted file mode 100644 index c94399b77b7..00000000000 --- a/third_party/Simple-web-server/repo/CMakeLists.txt +++ /dev/null @@ -1,52 +0,0 @@ -cmake_minimum_required (VERSION 2.8.8) -project (Simple-Web-Server) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wextra") - -include_directories(.) - -find_package(Threads REQUIRED) - -set(BOOST_COMPONENTS system thread filesystem date_time) -# Late 2017 TODO: remove the following checks and always use std::regex -if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) - set(BOOST_COMPONENTS ${BOOST_COMPONENTS} regex) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_BOOST_REGEX") - endif() -endif() -find_package(Boost 1.53.0 COMPONENTS ${BOOST_COMPONENTS} REQUIRED) -include_directories(SYSTEM ${Boost_INCLUDE_DIR}) - -if(APPLE) - set(OPENSSL_ROOT_DIR "/usr/local/opt/openssl") -endif() - -add_executable(http_examples http_examples.cpp) -target_link_libraries(http_examples ${Boost_LIBRARIES}) -target_link_libraries(http_examples ${CMAKE_THREAD_LIBS_INIT}) - -#TODO: add requirement for version 1.0.1g (can it be done in one line?) -find_package(OpenSSL) - -if(OPENSSL_FOUND) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_OPENSSL") - target_link_libraries(http_examples ${OPENSSL_LIBRARIES}) - include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR}) - - add_executable(https_examples https_examples.cpp) - target_link_libraries(https_examples ${Boost_LIBRARIES}) - target_link_libraries(https_examples ${OPENSSL_LIBRARIES}) - target_link_libraries(https_examples ${CMAKE_THREAD_LIBS_INIT}) -endif() - -if(MSYS) #TODO: Is MSYS true when MSVC is true? - target_link_libraries(http_examples ws2_32 wsock32) - if(OPENSSL_FOUND) - target_link_libraries(https_examples ws2_32 wsock32) - endif() -endif() - -enable_testing() -add_subdirectory(tests) - -install(FILES server_http.hpp client_http.hpp server_https.hpp client_https.hpp DESTINATION include/simple-web-server) diff --git a/third_party/Simple-web-server/repo/LICENSE b/third_party/Simple-web-server/repo/LICENSE deleted file mode 100644 index 7bfd646002c..00000000000 --- a/third_party/Simple-web-server/repo/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014-2016 Ole Christian Eidheim - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/third_party/Simple-web-server/repo/README.md b/third_party/Simple-web-server/repo/README.md deleted file mode 100644 index a7e15d72b37..00000000000 --- a/third_party/Simple-web-server/repo/README.md +++ /dev/null @@ -1,54 +0,0 @@ -Simple-Web-Server [![Build Status](https://travis-ci.org/eidheim/Simple-Web-Server.svg?branch=master)](https://travis-ci.org/eidheim/Simple-Web-Server) -================= - -A very simple, fast, multithreaded, platform independent HTTP and HTTPS server and client library implemented using C++11 and Boost.Asio. Created to be an easy way to make REST resources available from C++ applications. - -See https://github.com/eidheim/Simple-WebSocket-Server for an easy way to make WebSocket/WebSocket Secure endpoints in C++. Also, feel free to check out the new C++ IDE supporting C++11/14/17: https://github.com/cppit/jucipp. - -### Features - -* Asynchronous request handling -* Thread pool if needed -* Platform independent -* HTTPS support -* HTTP persistent connection (for HTTP/1.1) -* Client supports chunked transfer encoding -* Timeouts, if any of Server::timeout_request and Server::timeout_content are >0 (default: Server::timeout_request=5 seconds, and Server::timeout_content=300 seconds) -* Simple way to add REST resources using regex for path, and anonymous functions - -### Usage - -See http_examples.cpp or https_examples.cpp for example usage. - -See particularly the JSON-POST (using Boost.PropertyTree) and the GET /match/[number] examples, which are most relevant. - -### Dependencies - -* Boost C++ libraries -* For HTTPS: OpenSSL libraries - -### Compile and run - -Compile with a C++11 compliant compiler: -```sh -mkdir build -cd build -cmake .. -make -cd .. -``` - -#### HTTP - -Run the server and client examples: `./build/http_examples` - -Direct your favorite browser to for instance http://localhost:8080/ - -#### HTTPS - -Before running the server, an RSA private key (server.key) and an SSL certificate (server.crt) must be created. Follow, for instance, the instructions given here (for a self-signed certificate): http://www.akadia.com/services/ssh_test_certificate.html - -Run the server and client examples: `./build/https_examples` - -Direct your favorite browser to for instance https://localhost:8080/ - diff --git a/third_party/Simple-web-server/repo/client_http.hpp b/third_party/Simple-web-server/repo/client_http.hpp deleted file mode 100644 index b77fae7596b..00000000000 --- a/third_party/Simple-web-server/repo/client_http.hpp +++ /dev/null @@ -1,440 +0,0 @@ -#ifndef CLIENT_HTTP_HPP -#define CLIENT_HTTP_HPP - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#ifndef CASE_INSENSITIVE_EQUALS_AND_HASH -#define CASE_INSENSITIVE_EQUALS_AND_HASH -//Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html -class case_insensitive_equals { -public: - bool operator()(const std::string &key1, const std::string &key2) const { - return boost::algorithm::iequals(key1, key2); - } -}; -class case_insensitive_hash { -public: - size_t operator()(const std::string &key) const { - std::size_t seed=0; - for(auto &c: key) - boost::hash_combine(seed, std::tolower(c)); - return seed; - } -}; -#endif - -namespace SimpleWeb { - template - class Client; - - template - class ClientBase { - public: - virtual ~ClientBase() {} - - class Response { - friend class ClientBase; - friend class Client; - public: - std::string http_version, status_code; - - std::istream content; - - std::unordered_multimap header; - - private: - boost::asio::streambuf content_buffer; - - Response(): content(&content_buffer) {} - }; - - class Config { - friend class ClientBase; - private: - Config() {} - public: - /// Set timeout on requests in seconds. Default value: 0 (no timeout). - size_t timeout=0; - /// Set connect timeout in seconds. Default value: 0 (Config::timeout is then used instead). - size_t timeout_connect=0; - /// Set proxy server (server:port) - std::string proxy_server; - }; - - /// Set before calling request - Config config; - - std::shared_ptr request(const std::string& request_type, const std::string& path="/", boost::string_ref content="", - const std::map& header=std::map()) { - auto corrected_path=path; - if(corrected_path=="") - corrected_path="/"; - if(!config.proxy_server.empty() && std::is_same::value) - corrected_path="http://"+host+':'+std::to_string(port)+corrected_path; - - boost::asio::streambuf write_buffer; - std::ostream write_stream(&write_buffer); - write_stream << request_type << " " << corrected_path << " HTTP/1.1\r\n"; - write_stream << "Host: " << host << "\r\n"; - for(auto& h: header) { - write_stream << h.first << ": " << h.second << "\r\n"; - } - if(content.size()>0) - write_stream << "Content-Length: " << content.size() << "\r\n"; - write_stream << "\r\n"; - - connect(); - - auto timer=get_timeout_timer(); - boost::asio::async_write(*socket, write_buffer, - [this, &content, timer](const boost::system::error_code &ec, size_t /*bytes_transferred*/) { - if(timer) - timer->cancel(); - if(!ec) { - if(!content.empty()) { - auto timer=get_timeout_timer(); - boost::asio::async_write(*socket, boost::asio::buffer(content.data(), content.size()), - [this, timer](const boost::system::error_code &ec, size_t /*bytes_transferred*/) { - if(timer) - timer->cancel(); - if(ec) { - std::lock_guard lock(socket_mutex); - this->socket=nullptr; - throw boost::system::system_error(ec); - } - }); - } - } - else { - std::lock_guard lock(socket_mutex); - socket=nullptr; - throw boost::system::system_error(ec); - } - }); - io_service.reset(); - io_service.run(); - - return request_read(); - } - - std::shared_ptr request(const std::string& request_type, const std::string& path, std::iostream& content, - const std::map& header=std::map()) { - auto corrected_path=path; - if(corrected_path=="") - corrected_path="/"; - if(!config.proxy_server.empty() && std::is_same::value) - corrected_path="http://"+host+':'+std::to_string(port)+corrected_path; - - content.seekp(0, std::ios::end); - auto content_length=content.tellp(); - content.seekp(0, std::ios::beg); - - boost::asio::streambuf write_buffer; - std::ostream write_stream(&write_buffer); - write_stream << request_type << " " << corrected_path << " HTTP/1.1\r\n"; - write_stream << "Host: " << host << "\r\n"; - for(auto& h: header) { - write_stream << h.first << ": " << h.second << "\r\n"; - } - if(content_length>0) - write_stream << "Content-Length: " << content_length << "\r\n"; - write_stream << "\r\n"; - if(content_length>0) - write_stream << content.rdbuf(); - - connect(); - - auto timer=get_timeout_timer(); - boost::asio::async_write(*socket, write_buffer, - [this, timer](const boost::system::error_code &ec, size_t /*bytes_transferred*/) { - if(timer) - timer->cancel(); - if(ec) { - std::lock_guard lock(socket_mutex); - socket=nullptr; - throw boost::system::system_error(ec); - } - }); - io_service.reset(); - io_service.run(); - - return request_read(); - } - - void close() { - std::lock_guard lock(socket_mutex); - if(socket) { - boost::system::error_code ec; - socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - socket->lowest_layer().close(); - } - } - - protected: - boost::asio::io_service io_service; - boost::asio::ip::tcp::resolver resolver; - - std::unique_ptr socket; - std::mutex socket_mutex; - - std::string host; - unsigned short port; - - ClientBase(const std::string& host_port, unsigned short default_port) : resolver(io_service) { - auto parsed_host_port=parse_host_port(host_port, default_port); - host=parsed_host_port.first; - port=parsed_host_port.second; - } - - std::pair parse_host_port(const std::string &host_port, unsigned short default_port) { - std::pair parsed_host_port; - size_t host_end=host_port.find(':'); - if(host_end==std::string::npos) { - parsed_host_port.first=host_port; - parsed_host_port.second=default_port; - } - else { - parsed_host_port.first=host_port.substr(0, host_end); - parsed_host_port.second=static_cast(stoul(host_port.substr(host_end+1))); - } - return parsed_host_port; - } - - virtual void connect()=0; - - std::shared_ptr get_timeout_timer(size_t timeout=0) { - if(timeout==0) - timeout=config.timeout; - if(timeout==0) - return nullptr; - - auto timer=std::make_shared(io_service); - timer->expires_from_now(boost::posix_time::seconds(timeout)); - timer->async_wait([this](const boost::system::error_code& ec) { - if(!ec) { - close(); - } - }); - return timer; - } - - void parse_response_header(const std::shared_ptr &response) const { - std::string line; - getline(response->content, line); - size_t version_end=line.find(' '); - if(version_end!=std::string::npos) { - if(5http_version=line.substr(5, version_end-5); - if((version_end+1)status_code=line.substr(version_end+1, line.size()-(version_end+1)-1); - - getline(response->content, line); - size_t param_end; - while((param_end=line.find(':'))!=std::string::npos) { - size_t value_start=param_end+1; - if((value_start)header.insert(std::make_pair(line.substr(0, param_end), line.substr(value_start, line.size()-value_start-1))); - } - - getline(response->content, line); - } - } - } - - std::shared_ptr request_read() { - std::shared_ptr response(new Response()); - - boost::asio::streambuf chunked_streambuf; - - auto timer=get_timeout_timer(); - boost::asio::async_read_until(*socket, response->content_buffer, "\r\n\r\n", - [this, &response, &chunked_streambuf, timer](const boost::system::error_code& ec, size_t bytes_transferred) { - if(timer) - timer->cancel(); - if(!ec) { - size_t num_additional_bytes=response->content_buffer.size()-bytes_transferred; - - parse_response_header(response); - - auto header_it=response->header.find("Content-Length"); - if(header_it!=response->header.end()) { - auto content_length=stoull(header_it->second); - if(content_length>num_additional_bytes) { - auto timer=get_timeout_timer(); - boost::asio::async_read(*socket, response->content_buffer, - boost::asio::transfer_exactly(content_length-num_additional_bytes), - [this, timer](const boost::system::error_code& ec, size_t /*bytes_transferred*/) { - if(timer) - timer->cancel(); - if(ec) { - std::lock_guard lock(socket_mutex); - this->socket=nullptr; - throw boost::system::system_error(ec); - } - }); - } - } - else if((header_it=response->header.find("Transfer-Encoding"))!=response->header.end() && header_it->second=="chunked") { - request_read_chunked(response, chunked_streambuf); - } - else if(response->http_version<"1.1" || ((header_it=response->header.find("Connection"))!=response->header.end() && header_it->second=="close")) { - auto timer=get_timeout_timer(); - boost::asio::async_read(*socket, response->content_buffer, - [this, timer](const boost::system::error_code& ec, size_t /*bytes_transferred*/) { - if(timer) - timer->cancel(); - if(ec) { - std::lock_guard lock(socket_mutex); - this->socket=nullptr; - if(ec!=boost::asio::error::eof) - throw boost::system::system_error(ec); - } - }); - } - } - else { - std::lock_guard lock(socket_mutex); - socket=nullptr; - throw boost::system::system_error(ec); - } - }); - io_service.reset(); - io_service.run(); - - return response; - } - - void request_read_chunked(const std::shared_ptr &response, boost::asio::streambuf &streambuf) { - auto timer=get_timeout_timer(); - boost::asio::async_read_until(*socket, response->content_buffer, "\r\n", - [this, &response, &streambuf, timer](const boost::system::error_code& ec, size_t bytes_transferred) { - if(timer) - timer->cancel(); - if(!ec) { - std::string line; - getline(response->content, line); - bytes_transferred-=line.size()+1; - line.pop_back(); - std::streamsize length=stol(line, 0, 16); - - auto num_additional_bytes=static_cast(response->content_buffer.size()-bytes_transferred); - - auto post_process=[this, &response, &streambuf, length] { - std::ostream stream(&streambuf); - if(length>0) { - std::vector buffer(static_cast(length)); - response->content.read(&buffer[0], length); - stream.write(&buffer[0], length); - } - - //Remove "\r\n" - response->content.get(); - response->content.get(); - - if(length>0) - request_read_chunked(response, streambuf); - else { - std::ostream response_stream(&response->content_buffer); - response_stream << stream.rdbuf(); - } - }; - - if((2+length)>num_additional_bytes) { - auto timer=get_timeout_timer(); - boost::asio::async_read(*socket, response->content_buffer, - boost::asio::transfer_exactly(2+length-num_additional_bytes), - [this, post_process, timer](const boost::system::error_code& ec, size_t /*bytes_transferred*/) { - if(timer) - timer->cancel(); - if(!ec) { - post_process(); - } - else { - std::lock_guard lock(socket_mutex); - this->socket=nullptr; - throw boost::system::system_error(ec); - } - }); - } - else - post_process(); - } - else { - std::lock_guard lock(socket_mutex); - socket=nullptr; - throw boost::system::system_error(ec); - } - }); - } - }; - - template - class Client : public ClientBase {}; - - typedef boost::asio::ip::tcp::socket HTTP; - - template<> - class Client : public ClientBase { - public: - Client(const std::string& server_port_path) : ClientBase::ClientBase(server_port_path, 80) {} - - protected: - void connect() { - if(!socket || !socket->is_open()) { - std::unique_ptr query; - if(config.proxy_server.empty()) - query=std::unique_ptr(new boost::asio::ip::tcp::resolver::query(host, std::to_string(port))); - else { - auto proxy_host_port=parse_host_port(config.proxy_server, 8080); - query=std::unique_ptr(new boost::asio::ip::tcp::resolver::query(proxy_host_port.first, std::to_string(proxy_host_port.second))); - } - resolver.async_resolve(*query, [this](const boost::system::error_code &ec, - boost::asio::ip::tcp::resolver::iterator it){ - if(!ec) { - { - std::lock_guard lock(socket_mutex); - socket=std::unique_ptr(new HTTP(io_service)); - } - - auto timer=get_timeout_timer(config.timeout_connect); - boost::asio::async_connect(*socket, it, [this, timer] - (const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator /*it*/){ - if(timer) - timer->cancel(); - if(!ec) { - boost::asio::ip::tcp::no_delay option(true); - this->socket->set_option(option); - } - else { - std::lock_guard lock(socket_mutex); - this->socket=nullptr; - throw boost::system::system_error(ec); - } - }); - } - else { - std::lock_guard lock(socket_mutex); - socket=nullptr; - throw boost::system::system_error(ec); - } - }); - io_service.reset(); - io_service.run(); - } - } - }; -} - -#endif /* CLIENT_HTTP_HPP */ diff --git a/third_party/Simple-web-server/repo/client_https.hpp b/third_party/Simple-web-server/repo/client_https.hpp deleted file mode 100644 index 248f5b1ca90..00000000000 --- a/third_party/Simple-web-server/repo/client_https.hpp +++ /dev/null @@ -1,140 +0,0 @@ -#ifndef CLIENT_HTTPS_HPP -#define CLIENT_HTTPS_HPP - -#include "client_http.hpp" -#include - -namespace SimpleWeb { - typedef boost::asio::ssl::stream HTTPS; - - template<> - class Client : public ClientBase { - public: - Client(const std::string& server_port_path, bool verify_certificate=true, - const std::string& cert_file=std::string(), const std::string& private_key_file=std::string(), - const std::string& verify_file=std::string()) : - ClientBase::ClientBase(server_port_path, 443), context(boost::asio::ssl::context::tlsv12) { - if(cert_file.size()>0 && private_key_file.size()>0) { - context.use_certificate_chain_file(cert_file); - context.use_private_key_file(private_key_file, boost::asio::ssl::context::pem); - } - - if(verify_certificate) - context.set_verify_callback(boost::asio::ssl::rfc2818_verification(host)); - - if(verify_file.size()>0) - context.load_verify_file(verify_file); - else - context.set_default_verify_paths(); - - if(verify_file.size()>0 || verify_certificate) - context.set_verify_mode(boost::asio::ssl::verify_peer); - else - context.set_verify_mode(boost::asio::ssl::verify_none); - } - - protected: - boost::asio::ssl::context context; - - void connect() { - if(!socket || !socket->lowest_layer().is_open()) { - std::unique_ptr query; - if(config.proxy_server.empty()) - query=std::unique_ptr(new boost::asio::ip::tcp::resolver::query(host, std::to_string(port))); - else { - auto proxy_host_port=parse_host_port(config.proxy_server, 8080); - query=std::unique_ptr(new boost::asio::ip::tcp::resolver::query(proxy_host_port.first, std::to_string(proxy_host_port.second))); - } - resolver.async_resolve(*query, [this] - (const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator it){ - if(!ec) { - { - std::lock_guard lock(socket_mutex); - socket=std::unique_ptr(new HTTPS(io_service, context)); - } - - auto timer=get_timeout_timer(config.timeout_connect); - boost::asio::async_connect(socket->lowest_layer(), it, [this, timer] - (const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator /*it*/){ - if(timer) - timer->cancel(); - if(!ec) { - boost::asio::ip::tcp::no_delay option(true); - this->socket->lowest_layer().set_option(option); - } - else { - std::lock_guard lock(socket_mutex); - this->socket=nullptr; - throw boost::system::system_error(ec); - } - }); - } - else { - std::lock_guard lock(socket_mutex); - socket=nullptr; - throw boost::system::system_error(ec); - } - }); - io_service.reset(); - io_service.run(); - - if(!config.proxy_server.empty()) { - boost::asio::streambuf write_buffer; - std::ostream write_stream(&write_buffer); - auto host_port=host+':'+std::to_string(port); - write_stream << "CONNECT "+host_port+" HTTP/1.1\r\n" << "Host: " << host_port << "\r\n\r\n"; - auto timer=get_timeout_timer(); - boost::asio::async_write(socket->next_layer(), write_buffer, - [this, timer](const boost::system::error_code &ec, size_t /*bytes_transferred*/) { - if(timer) - timer->cancel(); - if(ec) { - std::lock_guard lock(socket_mutex); - socket=nullptr; - throw boost::system::system_error(ec); - } - }); - io_service.reset(); - io_service.run(); - - std::shared_ptr response(new Response()); - timer=get_timeout_timer(); - boost::asio::async_read_until(socket->next_layer(), response->content_buffer, "\r\n\r\n", - [this, timer](const boost::system::error_code& ec, size_t /*bytes_transferred*/) { - if(timer) - timer->cancel(); - if(ec) { - std::lock_guard lock(socket_mutex); - socket=nullptr; - throw boost::system::system_error(ec); - } - }); - io_service.reset(); - io_service.run(); - parse_response_header(response); - if (response->status_code.empty() || response->status_code.compare(0, 3, "200") != 0) { - std::lock_guard lock(socket_mutex); - socket=nullptr; - throw boost::system::system_error(boost::system::error_code(boost::system::errc::permission_denied, boost::system::generic_category())); - } - } - - auto timer=get_timeout_timer(); - this->socket->async_handshake(boost::asio::ssl::stream_base::client, - [this, timer](const boost::system::error_code& ec) { - if(timer) - timer->cancel(); - if(ec) { - std::lock_guard lock(socket_mutex); - socket=nullptr; - throw boost::system::system_error(ec); - } - }); - io_service.reset(); - io_service.run(); - } - } - }; -} - -#endif /* CLIENT_HTTPS_HPP */ diff --git a/third_party/Simple-web-server/repo/crypto.hpp b/third_party/Simple-web-server/repo/crypto.hpp deleted file mode 100644 index 268950d7e87..00000000000 --- a/third_party/Simple-web-server/repo/crypto.hpp +++ /dev/null @@ -1,220 +0,0 @@ -#ifndef CRYPTO_HPP -#define CRYPTO_HPP - -#include -#include -#include -#include -#include -#include - -//Moving these to a seperate namespace for minimal global namespace cluttering does not work with clang++ -#include -#include -#include -#include - -namespace SimpleWeb { - //TODO 2017: remove workaround for MSVS 2012 - #if _MSC_VER == 1700 //MSVS 2012 has no definition for round() - inline double round(double x) { //custom definition of round() for positive numbers - return floor(x + 0.5); - } - #endif - - class Crypto { - const static size_t buffer_size=131072; - public: - class Base64 { - public: - static std::string encode(const std::string &ascii) { - std::string base64; - - BIO *bio, *b64; - BUF_MEM *bptr=BUF_MEM_new(); - - b64 = BIO_new(BIO_f_base64()); - BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); - bio = BIO_new(BIO_s_mem()); - BIO_push(b64, bio); - BIO_set_mem_buf(b64, bptr, BIO_CLOSE); - - //Write directly to base64-buffer to avoid copy - int base64_length=static_cast(round(4*ceil((double)ascii.size()/3.0))); - base64.resize(base64_length); - bptr->length=0; - bptr->max=base64_length+1; - bptr->data=(char*)&base64[0]; - - BIO_write(b64, &ascii[0], static_cast(ascii.size())); - BIO_flush(b64); - - //To keep &base64[0] through BIO_free_all(b64) - bptr->length=0; - bptr->max=0; - bptr->data=nullptr; - - BIO_free_all(b64); - - return base64; - } - - static std::string decode(const std::string &base64) { - std::string ascii; - - //Resize ascii, however, the size is a up to two bytes too large. - ascii.resize((6*base64.size())/8); - BIO *b64, *bio; - - b64 = BIO_new(BIO_f_base64()); - BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); - bio = BIO_new_mem_buf((char*)&base64[0], static_cast(base64.size())); - bio = BIO_push(b64, bio); - - int decoded_length = BIO_read(bio, &ascii[0], static_cast(ascii.size())); - ascii.resize(decoded_length); - - BIO_free_all(b64); - - return ascii; - } - }; - - /// Return hex string from bytes in input string. - static std::string to_hex_string(const std::string &input) { - std::stringstream hex_stream; - hex_stream << std::hex << std::internal << std::setfill('0'); - for (auto &byte : input) - hex_stream << std::setw(2) << static_cast(static_cast(byte)); - return hex_stream.str(); - } - - static std::string md5(const std::string &input, size_t iterations=1) { - std::string hash; - - hash.resize(128 / 8); - MD5(reinterpret_cast(&input[0]), input.size(), reinterpret_cast(&hash[0])); - - for (size_t c = 1; c < iterations; ++c) - MD5(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); - - return hash; - } - - static std::string md5(std::istream &stream, size_t iterations=1) { - MD5_CTX context; - MD5_Init(&context); - std::streamsize read_length; - std::vector buffer(buffer_size); - while((read_length=stream.read(&buffer[0], buffer_size).gcount())>0) - MD5_Update(&context, buffer.data(), read_length); - std::string hash; - hash.resize(128 / 8); - MD5_Final(reinterpret_cast(&hash[0]), &context); - - for (size_t c = 1; c < iterations; ++c) - MD5(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); - - return hash; - } - - static std::string sha1(const std::string &input, size_t iterations=1) { - std::string hash; - - hash.resize(160 / 8); - SHA1(reinterpret_cast(&input[0]), input.size(), reinterpret_cast(&hash[0])); - - for (size_t c = 1; c < iterations; ++c) - SHA1(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); - - return hash; - } - - static std::string sha1(std::istream &stream, size_t iterations=1) { - SHA_CTX context; - SHA1_Init(&context); - std::streamsize read_length; - std::vector buffer(buffer_size); - while((read_length=stream.read(&buffer[0], buffer_size).gcount())>0) - SHA1_Update(&context, buffer.data(), read_length); - std::string hash; - hash.resize(160 / 8); - SHA1_Final(reinterpret_cast(&hash[0]), &context); - - for (size_t c = 1; c < iterations; ++c) - SHA1(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); - - return hash; - } - - static std::string sha256(const std::string &input, size_t iterations=1) { - std::string hash; - - hash.resize(256 / 8); - SHA256(reinterpret_cast(&input[0]), input.size(), reinterpret_cast(&hash[0])); - - for (size_t c = 1; c < iterations; ++c) - SHA256(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); - - return hash; - } - - static std::string sha256(std::istream &stream, size_t iterations=1) { - SHA256_CTX context; - SHA256_Init(&context); - std::streamsize read_length; - std::vector buffer(buffer_size); - while((read_length=stream.read(&buffer[0], buffer_size).gcount())>0) - SHA256_Update(&context, buffer.data(), read_length); - std::string hash; - hash.resize(256 / 8); - SHA256_Final(reinterpret_cast(&hash[0]), &context); - - for (size_t c = 1; c < iterations; ++c) - SHA256(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); - - return hash; - } - - static std::string sha512(const std::string &input, size_t iterations=1) { - std::string hash; - - hash.resize(512 / 8); - SHA512(reinterpret_cast(&input[0]), input.size(), reinterpret_cast(&hash[0])); - - for (size_t c = 1; c < iterations; ++c) - SHA512(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); - - return hash; - } - - static std::string sha512(std::istream &stream, size_t iterations=1) { - SHA512_CTX context; - SHA512_Init(&context); - std::streamsize read_length; - std::vector buffer(buffer_size); - while((read_length=stream.read(&buffer[0], buffer_size).gcount())>0) - SHA512_Update(&context, buffer.data(), read_length); - std::string hash; - hash.resize(512 / 8); - SHA512_Final(reinterpret_cast(&hash[0]), &context); - - for (size_t c = 1; c < iterations; ++c) - SHA512(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); - - return hash; - } - - /// key_size is number of bytes of the returned key. - static std::string pbkdf2(const std::string &password, const std::string &salt, int iterations, int key_size) { - std::string key; - key.resize(key_size); - PKCS5_PBKDF2_HMAC_SHA1(password.c_str(), password.size(), - reinterpret_cast(salt.c_str()), salt.size(), iterations, - key_size, reinterpret_cast(&key[0])); - return key; - } - }; -} -#endif /* CRYPTO_HPP */ - diff --git a/third_party/Simple-web-server/repo/http_examples.cpp b/third_party/Simple-web-server/repo/http_examples.cpp deleted file mode 100644 index 60d13111dd8..00000000000 --- a/third_party/Simple-web-server/repo/http_examples.cpp +++ /dev/null @@ -1,220 +0,0 @@ -#include "server_http.hpp" -#include "client_http.hpp" - -//Added for the json-example -#define BOOST_SPIRIT_THREADSAFE -#include -#include - -//Added for the default_resource example -#include -#include -#include -#include -#ifdef HAVE_OPENSSL -#include "crypto.hpp" -#endif - -using namespace std; -//Added for the json-example: -using namespace boost::property_tree; - -typedef SimpleWeb::Server HttpServer; -typedef SimpleWeb::Client HttpClient; - -//Added for the default_resource example -void default_resource_send(const HttpServer &server, const shared_ptr &response, - const shared_ptr &ifs); - -int main() { - //HTTP-server at port 8080 using 1 thread - //Unless you do more heavy non-threaded processing in the resources, - //1 thread is usually faster than several threads - HttpServer server; - server.config.port=80; - - //Add resources using path-regex and method-string, and an anonymous function - //POST-example for the path /string, responds the posted string - server.resource["^/string$"]["POST"]=[](shared_ptr response, shared_ptr request) { - //Retrieve string: - auto content=request->content.string(); - //request->content.string() is a convenience function for: - //stringstream ss; - //ss << request->content.rdbuf(); - //string content=ss.str(); - - *response << "HTTP/1.1 200 OK\r\nContent-Length: " << content.length() << "\r\n\r\n" << content; - }; - - //POST-example for the path /json, responds firstName+" "+lastName from the posted json - //Responds with an appropriate error message if the posted json is not valid, or if firstName or lastName is missing - //Example posted json: - //{ - // "firstName": "John", - // "lastName": "Smith", - // "age": 25 - //} - server.resource["^/json$"]["POST"]=[](shared_ptr response, shared_ptr request) { - try { - ptree pt; - read_json(request->content, pt); - - string name=pt.get("firstName")+" "+pt.get("lastName"); - - *response << "HTTP/1.1 200 OK\r\n" - << "Content-Type: application/json\r\n" - << "Content-Length: " << name.length() << "\r\n\r\n" - << name; - } - catch(exception& e) { - *response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << strlen(e.what()) << "\r\n\r\n" << e.what(); - } - }; - - //GET-example for the path /info - //Responds with request-information - server.resource["^/info$"]["GET"]=[](shared_ptr response, shared_ptr request) { - stringstream content_stream; - content_stream << "

Request from " << request->remote_endpoint_address << " (" << request->remote_endpoint_port << ")

"; - content_stream << request->method << " " << request->path << " HTTP/" << request->http_version << "
"; - for(auto& header: request->header) { - content_stream << header.first << ": " << header.second << "
"; - } - - //find length of content_stream (length received using content_stream.tellp()) - content_stream.seekp(0, ios::end); - - *response << "HTTP/1.1 200 OK\r\nContent-Length: " << content_stream.tellp() << "\r\n\r\n" << content_stream.rdbuf(); - }; - - //GET-example for the path /match/[number], responds with the matched string in path (number) - //For instance a request GET /match/123 will receive: 123 - server.resource["^/match/([0-9]+)$"]["GET"]=[&server](shared_ptr response, shared_ptr request) { - string number=request->path_match[1]; - *response << "HTTP/1.1 200 OK\r\nContent-Length: " << number.length() << "\r\n\r\n" << number; - }; - - //Get example simulating heavy work in a separate thread - server.resource["^/work$"]["GET"]=[&server](shared_ptr response, shared_ptr /*request*/) { - thread work_thread([response] { - this_thread::sleep_for(chrono::seconds(5)); - string message="Work done"; - *response << "HTTP/1.1 200 OK\r\nContent-Length: " << message.length() << "\r\n\r\n" << message; - }); - work_thread.detach(); - }; - - server.resource["^/getAvailableNetwork$"]["GET"]=[&server](shared_ptr response, shared_ptr /*request*/) { - thread work_thread([response] { - this_thread::sleep_for(chrono::seconds(5)); - // string message="Work done"; - string json_string="{\"result\":[{\"ch\":13,\"ha\":\"18B43000003D2785\",\"nn\":\"NEST-PAN-C1E7\",\"pi\":\"0xC19B\",\"xp\":\"EEA74CE1EDFA2E8A\"}]}"; - // string json_string="{\"firstName\": \"John\",\"lastName\": \"Smith\",\"age\": 25}"; - *response << "HTTP/1.1 200 OK\r\nContent-Length: " << json_string.length() << "\r\nContent-Type:application/json; charset=utf-8" << "\r\n\r\n" << json_string; - }); - work_thread.detach(); - }; - - //Default GET-example. If no other matches, this anonymous function will be called. - //Will respond with content in the web/-directory, and its subdirectories. - //Default file: index.html - //Can for instance be used to retrieve an HTML 5 client that uses REST-resources on this server - server.default_resource["GET"]=[&server](shared_ptr response, shared_ptr request) { - try { - auto web_root_path=boost::filesystem::canonical("web"); - auto path=boost::filesystem::canonical(web_root_path/request->path); - //Check if path is within web_root_path - if(distance(web_root_path.begin(), web_root_path.end())>distance(path.begin(), path.end()) || - !equal(web_root_path.begin(), web_root_path.end(), path.begin())) - throw invalid_argument("path must be within root path"); - if(boost::filesystem::is_directory(path)) - path/="index.html"; - if(!(boost::filesystem::exists(path) && boost::filesystem::is_regular_file(path))) - throw invalid_argument("file does not exist"); - - std::string cache_control, etag; - - // Uncomment the following line to enable Cache-Control - // cache_control="Cache-Control: max-age=86400\r\n"; - -#ifdef HAVE_OPENSSL - // Uncomment the following lines to enable ETag - // { - // ifstream ifs(path.string(), ifstream::in | ios::binary); - // if(ifs) { - // auto hash=SimpleWeb::Crypto::to_hex_string(SimpleWeb::Crypto::md5(ifs)); - // etag = "ETag: \""+hash+"\"\r\n"; - // auto it=request->header.find("If-None-Match"); - // if(it!=request->header.end()) { - // if(!it->second.empty() && it->second.compare(1, hash.size(), hash)==0) { - // *response << "HTTP/1.1 304 Not Modified\r\n" << cache_control << etag << "\r\n\r\n"; - // return; - // } - // } - // } - // else - // throw invalid_argument("could not read file"); - // } -#endif - - auto ifs=make_shared(); - ifs->open(path.string(), ifstream::in | ios::binary | ios::ate); - - if(*ifs) { - auto length=ifs->tellg(); - ifs->seekg(0, ios::beg); - - *response << "HTTP/1.1 200 OK\r\n" << cache_control << etag << "Content-Length: " << length << "\r\n\r\n"; - default_resource_send(server, response, ifs); - } - else - throw invalid_argument("could not read file"); - } - catch(const exception &e) { - string content="Could not open path "+request->path+": "+e.what(); - *response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << content.length() << "\r\n\r\n" << content; - } - }; - - thread server_thread([&server](){ - //Start server - server.start(); - }); - - //Wait for server to start so that the client can connect - this_thread::sleep_for(chrono::seconds(1)); - - // //Client examples - // HttpClient client("localhost:8080"); - // auto r1=client.request("GET", "/match/123"); - // cout << r1->content.rdbuf() << endl; - - // string json_string="{\"firstName\": \"John\",\"lastName\": \"Smith\",\"age\": 25}"; - // auto r2=client.request("POST", "/string", json_string); - // cout << r2->content.rdbuf() << endl; - - // auto r3=client.request("POST", "/json", json_string); - // cout << r3->content.rdbuf() << endl; - - server_thread.join(); - - return 0; -} - -void default_resource_send(const HttpServer &server, const shared_ptr &response, - const shared_ptr &ifs) { - //read and send 128 KB at a time - static vector buffer(131072); // Safe when server is running on one thread - streamsize read_length; - if((read_length=ifs->read(&buffer[0], buffer.size()).gcount())>0) { - response->write(&buffer[0], read_length); - if(read_length==static_cast(buffer.size())) { - server.send(response, [&server, response, ifs](const boost::system::error_code &ec) { - if(!ec) - default_resource_send(server, response, ifs); - else - cerr << "Connection interrupted" << endl; - }); - } - } -} diff --git a/third_party/Simple-web-server/repo/https_examples.cpp b/third_party/Simple-web-server/repo/https_examples.cpp deleted file mode 100644 index 88621709d24..00000000000 --- a/third_party/Simple-web-server/repo/https_examples.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include "server_https.hpp" -#include "client_https.hpp" - -//Added for the json-example -#define BOOST_SPIRIT_THREADSAFE -#include -#include - -//Added for the default_resource example -#include -#include -#include -#include -#include "crypto.hpp" - -using namespace std; -//Added for the json-example: -using namespace boost::property_tree; - -typedef SimpleWeb::Server HttpsServer; -typedef SimpleWeb::Client HttpsClient; - -//Added for the default_resource example -void default_resource_send(const HttpsServer &server, const shared_ptr &response, - const shared_ptr &ifs); - -int main() { - //HTTPS-server at port 8080 using 1 thread - //Unless you do more heavy non-threaded processing in the resources, - //1 thread is usually faster than several threads - HttpsServer server("server.crt", "server.key"); - server.config.port=8080; - - //Add resources using path-regex and method-string, and an anonymous function - //POST-example for the path /string, responds the posted string - server.resource["^/string$"]["POST"]=[](shared_ptr response, shared_ptr request) { - //Retrieve string: - auto content=request->content.string(); - //request->content.string() is a convenience function for: - //stringstream ss; - //ss << request->content.rdbuf(); - //string content=ss.str(); - - *response << "HTTP/1.1 200 OK\r\nContent-Length: " << content.length() << "\r\n\r\n" << content; - }; - - //POST-example for the path /json, responds firstName+" "+lastName from the posted json - //Responds with an appropriate error message if the posted json is not valid, or if firstName or lastName is missing - //Example posted json: - //{ - // "firstName": "John", - // "lastName": "Smith", - // "age": 25 - //} - server.resource["^/json$"]["POST"]=[](shared_ptr response, shared_ptr request) { - try { - ptree pt; - read_json(request->content, pt); - - string name=pt.get("firstName")+" "+pt.get("lastName"); - - *response << "HTTP/1.1 200 OK\r\n" - << "Content-Type: application/json\r\n" - << "Content-Length: " << name.length() << "\r\n\r\n" - << name; - } - catch(exception& e) { - *response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << strlen(e.what()) << "\r\n\r\n" << e.what(); - } - }; - - //GET-example for the path /info - //Responds with request-information - server.resource["^/info$"]["GET"]=[](shared_ptr response, shared_ptr request) { - stringstream content_stream; - content_stream << "

Request from " << request->remote_endpoint_address << " (" << request->remote_endpoint_port << ")

"; - content_stream << request->method << " " << request->path << " HTTP/" << request->http_version << "
"; - for(auto& header: request->header) { - content_stream << header.first << ": " << header.second << "
"; - } - - //find length of content_stream (length received using content_stream.tellp()) - content_stream.seekp(0, ios::end); - - *response << "HTTP/1.1 200 OK\r\nContent-Length: " << content_stream.tellp() << "\r\n\r\n" << content_stream.rdbuf(); - }; - - //GET-example for the path /match/[number], responds with the matched string in path (number) - //For instance a request GET /match/123 will receive: 123 - server.resource["^/match/([0-9]+)$"]["GET"]=[&server](shared_ptr response, shared_ptr request) { - string number=request->path_match[1]; - *response << "HTTP/1.1 200 OK\r\nContent-Length: " << number.length() << "\r\n\r\n" << number; - }; - - //Get example simulating heavy work in a separate thread - server.resource["^/work$"]["GET"]=[&server](shared_ptr response, shared_ptr /*request*/) { - thread work_thread([response] { - this_thread::sleep_for(chrono::seconds(5)); - string message="Work done"; - *response << "HTTP/1.1 200 OK\r\nContent-Length: " << message.length() << "\r\n\r\n" << message; - }); - work_thread.detach(); - }; - - //Default GET-example. If no other matches, this anonymous function will be called. - //Will respond with content in the web/-directory, and its subdirectories. - //Default file: index.html - //Can for instance be used to retrieve an HTML 5 client that uses REST-resources on this server - server.default_resource["GET"]=[&server](shared_ptr response, shared_ptr request) { - try { - auto web_root_path=boost::filesystem::canonical("web"); - auto path=boost::filesystem::canonical(web_root_path/request->path); - //Check if path is within web_root_path - if(distance(web_root_path.begin(), web_root_path.end())>distance(path.begin(), path.end()) || - !equal(web_root_path.begin(), web_root_path.end(), path.begin())) - throw invalid_argument("path must be within root path"); - if(boost::filesystem::is_directory(path)) - path/="index.html"; - if(!(boost::filesystem::exists(path) && boost::filesystem::is_regular_file(path))) - throw invalid_argument("file does not exist"); - - std::string cache_control, etag; - - // Uncomment the following line to enable Cache-Control - // cache_control="Cache-Control: max-age=86400\r\n"; - - // Uncomment the following lines to enable ETag - // { - // ifstream ifs(path.string(), ifstream::in | ios::binary); - // if(ifs) { - // auto hash=SimpleWeb::Crypto::to_hex_string(SimpleWeb::Crypto::md5(ifs)); - // etag = "ETag: \""+hash+"\"\r\n"; - // auto it=request->header.find("If-None-Match"); - // if(it!=request->header.end()) { - // if(!it->second.empty() && it->second.compare(1, hash.size(), hash)==0) { - // *response << "HTTP/1.1 304 Not Modified\r\n" << cache_control << etag << "\r\n\r\n"; - // return; - // } - // } - // } - // else - // throw invalid_argument("could not read file"); - // } - - auto ifs=make_shared(); - ifs->open(path.string(), ifstream::in | ios::binary | ios::ate); - - if(*ifs) { - auto length=ifs->tellg(); - ifs->seekg(0, ios::beg); - - *response << "HTTP/1.1 200 OK\r\n" << cache_control << etag << "Content-Length: " << length << "\r\n\r\n"; - default_resource_send(server, response, ifs); - } - else - throw invalid_argument("could not read file"); - } - catch(const exception &e) { - string content="Could not open path "+request->path+": "+e.what(); - *response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << content.length() << "\r\n\r\n" << content; - } - }; - - thread server_thread([&server](){ - //Start server - server.start(); - }); - - //Wait for server to start so that the client can connect - this_thread::sleep_for(chrono::seconds(1)); - - //Client examples - //Second Client() parameter set to false: no certificate verification - HttpsClient client("localhost:8080", false); - auto r1=client.request("GET", "/match/123"); - cout << r1->content.rdbuf() << endl; - - string json_string="{\"firstName\": \"John\",\"lastName\": \"Smith\",\"age\": 25}"; - auto r2=client.request("POST", "/string", json_string); - cout << r2->content.rdbuf() << endl; - - auto r3=client.request("POST", "/json", json_string); - cout << r3->content.rdbuf() << endl; - - server_thread.join(); - - return 0; -} - -void default_resource_send(const HttpsServer &server, const shared_ptr &response, - const shared_ptr &ifs) { - //read and send 128 KB at a time - static vector buffer(131072); // Safe when server is running on one thread - streamsize read_length; - if((read_length=ifs->read(&buffer[0], buffer.size()).gcount())>0) { - response->write(&buffer[0], read_length); - if(read_length==static_cast(buffer.size())) { - server.send(response, [&server, response, ifs](const boost::system::error_code &ec) { - if(!ec) - default_resource_send(server, response, ifs); - else - cerr << "Connection interrupted" << endl; - }); - } - } -} diff --git a/third_party/Simple-web-server/repo/server_http.hpp b/third_party/Simple-web-server/repo/server_http.hpp deleted file mode 100644 index de4074300eb..00000000000 --- a/third_party/Simple-web-server/repo/server_http.hpp +++ /dev/null @@ -1,465 +0,0 @@ -#ifndef SERVER_HTTP_HPP -#define SERVER_HTTP_HPP - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#ifndef CASE_INSENSITIVE_EQUALS_AND_HASH -#define CASE_INSENSITIVE_EQUALS_AND_HASH -//Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html -class case_insensitive_equals { -public: - bool operator()(const std::string &key1, const std::string &key2) const { - return boost::algorithm::iequals(key1, key2); - } -}; -class case_insensitive_hash { -public: - size_t operator()(const std::string &key) const { - std::size_t seed=0; - for(auto &c: key) - boost::hash_combine(seed, std::tolower(c)); - return seed; - } -}; -#endif - -// Late 2017 TODO: remove the following checks and always use std::regex -#ifdef USE_BOOST_REGEX -#include -#define REGEX_NS boost -#else -#include -#define REGEX_NS std -#endif - -// TODO when switching to c++14, use [[deprecated]] instead -#ifndef DEPRECATED -#ifdef __GNUC__ -#define DEPRECATED __attribute__((deprecated)) -#elif defined(_MSC_VER) -#define DEPRECATED __declspec(deprecated) -#else -#define DEPRECATED -#endif -#endif - -namespace SimpleWeb { - template - class Server; - - template - class ServerBase { - public: - virtual ~ServerBase() {} - - class Response : public std::ostream { - friend class ServerBase; - - boost::asio::streambuf streambuf; - - std::shared_ptr socket; - - Response(const std::shared_ptr &socket): std::ostream(&streambuf), socket(socket) {} - - public: - size_t size() { - return streambuf.size(); - } - - /// If true, force server to close the connection after the response have been sent. - /// - /// This is useful when implementing a HTTP/1.0-server sending content - /// without specifying the content length. - bool close_connection_after_response = false; - }; - - class Content : public std::istream { - friend class ServerBase; - public: - size_t size() { - return streambuf.size(); - } - std::string string() { - std::stringstream ss; - ss << rdbuf(); - return ss.str(); - } - private: - boost::asio::streambuf &streambuf; - Content(boost::asio::streambuf &streambuf): std::istream(&streambuf), streambuf(streambuf) {} - }; - - class Request { - friend class ServerBase; - friend class Server; - public: - std::string method, path, http_version; - - Content content; - - std::unordered_multimap header; - - REGEX_NS::smatch path_match; - - std::string remote_endpoint_address; - unsigned short remote_endpoint_port; - - private: - Request(const socket_type &socket): content(streambuf) { - try { - remote_endpoint_address=socket.lowest_layer().remote_endpoint().address().to_string(); - remote_endpoint_port=socket.lowest_layer().remote_endpoint().port(); - } - catch(...) {} - } - - boost::asio::streambuf streambuf; - }; - - class Config { - friend class ServerBase; - - Config(unsigned short port): port(port) {} - public: - /// Port number to use. Defaults to 80 for HTTP and 443 for HTTPS. - unsigned short port; - /// Number of threads that the server will use when start() is called. Defaults to 1 thread. - size_t thread_pool_size=1; - /// Timeout on request handling. Defaults to 5 seconds. - size_t timeout_request=5; - /// Timeout on content handling. Defaults to 300 seconds. - size_t timeout_content=300; - /// IPv4 address in dotted decimal form or IPv6 address in hexadecimal notation. - /// If empty, the address will be any address. - std::string address; - /// Set to false to avoid binding the socket to an address that is already in use. Defaults to true. - bool reuse_address=true; - }; - ///Set before calling start(). - Config config; - - private: - class regex_orderable : public REGEX_NS::regex { - std::string str; - public: - regex_orderable(const char *regex_cstr) : REGEX_NS::regex(regex_cstr), str(regex_cstr) {} - regex_orderable(const std::string ®ex_str) : REGEX_NS::regex(regex_str), str(regex_str) {} - bool operator<(const regex_orderable &rhs) const { - return str::Response>, std::shared_ptr::Request>)> > > resource; - - std::map::Response>, std::shared_ptr::Request>)> > default_resource; - - std::function::Request>, const boost::system::error_code&)> on_error; - - std::function socket, std::shared_ptr::Request>)> on_upgrade; - - virtual void start() { - if(!io_service) - io_service=std::make_shared(); - - if(io_service->stopped()) - io_service->reset(); - - boost::asio::ip::tcp::endpoint endpoint; - if(config.address.size()>0) - endpoint=boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(config.address), config.port); - else - endpoint=boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), config.port); - - if(!acceptor) - acceptor=std::unique_ptr(new boost::asio::ip::tcp::acceptor(*io_service)); - acceptor->open(endpoint.protocol()); - acceptor->set_option(boost::asio::socket_base::reuse_address(config.reuse_address)); - acceptor->bind(endpoint); - acceptor->listen(); - - accept(); - - //If thread_pool_size>1, start m_io_service.run() in (thread_pool_size-1) threads for thread-pooling - threads.clear(); - for(size_t c=1;crun(); - }); - } - - //Main thread - if(config.thread_pool_size>0) - io_service->run(); - - //Wait for the rest of the threads, if any, to finish as well - for(auto& t: threads) { - t.join(); - } - } - - void stop() { - acceptor->close(); - if(config.thread_pool_size>0) - io_service->stop(); - } - - ///Use this function if you need to recursively send parts of a longer message - void send(const std::shared_ptr &response, const std::function& callback=nullptr) const { - boost::asio::async_write(*response->socket, response->streambuf, [this, response, callback](const boost::system::error_code& ec, size_t /*bytes_transferred*/) { - if(callback) - callback(ec); - }); - } - - /// If you have your own boost::asio::io_service, store its pointer here before running start(). - /// You might also want to set config.thread_pool_size to 0. - std::shared_ptr io_service; - protected: - std::unique_ptr acceptor; - std::vector threads; - - ServerBase(unsigned short port) : config(port) {} - - virtual void accept()=0; - - std::shared_ptr get_timeout_timer(const std::shared_ptr &socket, long seconds) { - if(seconds==0) - return nullptr; - - auto timer=std::make_shared(*io_service); - timer->expires_from_now(boost::posix_time::seconds(seconds)); - timer->async_wait([socket](const boost::system::error_code& ec){ - if(!ec) { - boost::system::error_code ec; - socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - socket->lowest_layer().close(); - } - }); - return timer; - } - - void read_request_and_content(const std::shared_ptr &socket) { - //Create new streambuf (Request::streambuf) for async_read_until() - //shared_ptr is used to pass temporary objects to the asynchronous functions - std::shared_ptr request(new Request(*socket)); - - //Set timeout on the following boost::asio::async-read or write function - auto timer=this->get_timeout_timer(socket, config.timeout_request); - - boost::asio::async_read_until(*socket, request->streambuf, "\r\n\r\n", - [this, socket, request, timer](const boost::system::error_code& ec, size_t bytes_transferred) { - if(timer) - timer->cancel(); - if(!ec) { - //request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs: - //"After a successful async_read_until operation, the streambuf may contain additional data beyond the delimiter" - //The chosen solution is to extract lines from the stream directly when parsing the header. What is left of the - //streambuf (maybe some bytes of the content) is appended to in the async_read-function below (for retrieving content). - size_t num_additional_bytes=request->streambuf.size()-bytes_transferred; - - if(!this->parse_request(request)) - return; - - //If content, read that as well - auto it=request->header.find("Content-Length"); - if(it!=request->header.end()) { - unsigned long long content_length; - try { - content_length=stoull(it->second); - } - catch(const std::exception &e) { - if(on_error) - on_error(request, boost::system::error_code(boost::system::errc::protocol_error, boost::system::generic_category())); - return; - } - if(content_length>num_additional_bytes) { - //Set timeout on the following boost::asio::async-read or write function - auto timer=this->get_timeout_timer(socket, config.timeout_content); - boost::asio::async_read(*socket, request->streambuf, - boost::asio::transfer_exactly(content_length-num_additional_bytes), - [this, socket, request, timer] - (const boost::system::error_code& ec, size_t /*bytes_transferred*/) { - if(timer) - timer->cancel(); - if(!ec) - this->find_resource(socket, request); - else if(on_error) - on_error(request, ec); - }); - } - else - this->find_resource(socket, request); - } - else - this->find_resource(socket, request); - } - else if(on_error) - on_error(request, ec); - }); - } - - bool parse_request(const std::shared_ptr &request) const { - std::string line; - getline(request->content, line); - size_t method_end; - if((method_end=line.find(' '))!=std::string::npos) { - size_t path_end; - if((path_end=line.find(' ', method_end+1))!=std::string::npos) { - request->method=line.substr(0, method_end); - request->path=line.substr(method_end+1, path_end-method_end-1); - - size_t protocol_end; - if((protocol_end=line.find('/', path_end+1))!=std::string::npos) { - if(line.compare(path_end+1, protocol_end-path_end-1, "HTTP")!=0) - return false; - request->http_version=line.substr(protocol_end+1, line.size()-protocol_end-2); - } - else - return false; - - getline(request->content, line); - size_t param_end; - while((param_end=line.find(':'))!=std::string::npos) { - size_t value_start=param_end+1; - if((value_start)header.emplace(line.substr(0, param_end), line.substr(value_start, line.size()-value_start-1)); - } - - getline(request->content, line); - } - } - else - return false; - } - else - return false; - return true; - } - - void find_resource(const std::shared_ptr &socket, const std::shared_ptr &request) { - //Upgrade connection - if(on_upgrade) { - auto it=request->header.find("Upgrade"); - if(it!=request->header.end()) { - on_upgrade(socket, request); - return; - } - } - //Find path- and method-match, and call write_response - for(auto ®ex_method: resource) { - auto it=regex_method.second.find(request->method); - if(it!=regex_method.second.end()) { - REGEX_NS::smatch sm_res; - if(REGEX_NS::regex_match(request->path, sm_res, regex_method.first)) { - request->path_match=std::move(sm_res); - write_response(socket, request, it->second); - return; - } - } - } - auto it=default_resource.find(request->method); - if(it!=default_resource.end()) { - write_response(socket, request, it->second); - } - } - - void write_response(const std::shared_ptr &socket, const std::shared_ptr &request, - std::function::Response>, - std::shared_ptr::Request>)>& resource_function) { - //Set timeout on the following boost::asio::async-read or write function - auto timer=this->get_timeout_timer(socket, config.timeout_content); - - auto response=std::shared_ptr(new Response(socket), [this, request, timer](Response *response_ptr) { - auto response=std::shared_ptr(response_ptr); - this->send(response, [this, response, request, timer](const boost::system::error_code& ec) { - if(timer) - timer->cancel(); - if(!ec) { - if (response->close_connection_after_response) - return; - - auto range=request->header.equal_range("Connection"); - for(auto it=range.first;it!=range.second;it++) { - if(boost::iequals(it->second, "close")) { - return; - } else if (boost::iequals(it->second, "keep-alive")) { - this->read_request_and_content(response->socket); - return; - } - } - if(request->http_version >= "1.1") - this->read_request_and_content(response->socket); - } - else if(on_error) - on_error(request, ec); - }); - }); - - try { - resource_function(response, request); - } - catch(const std::exception &e) { - if(on_error) - on_error(request, boost::system::error_code(boost::system::errc::operation_canceled, boost::system::generic_category())); - return; - } - } - }; - - template - class Server : public ServerBase {}; - - typedef boost::asio::ip::tcp::socket HTTP; - - template<> - class Server : public ServerBase { - public: - DEPRECATED Server(unsigned short port, size_t thread_pool_size=1, long timeout_request=5, long timeout_content=300) : - Server() { - config.port=port; - config.thread_pool_size=thread_pool_size; - config.timeout_request=timeout_request; - config.timeout_content=timeout_content; - } - - Server() : ServerBase::ServerBase(80) {} - - protected: - void accept() { - //Create new socket for this connection - //Shared_ptr is used to pass temporary objects to the asynchronous functions - auto socket=std::make_shared(*io_service); - - acceptor->async_accept(*socket, [this, socket](const boost::system::error_code& ec){ - //Immediately start accepting a new connection (if io_service hasn't been stopped) - if (ec != boost::asio::error::operation_aborted) - accept(); - - if(!ec) { - boost::asio::ip::tcp::no_delay option(true); - socket->set_option(option); - - this->read_request_and_content(socket); - } - else if(on_error) - on_error(std::shared_ptr(new Request(*socket)), ec); - }); - } - }; -} -#endif /* SERVER_HTTP_HPP */ diff --git a/third_party/Simple-web-server/repo/server_https.hpp b/third_party/Simple-web-server/repo/server_https.hpp deleted file mode 100644 index 309a5ddd8a9..00000000000 --- a/third_party/Simple-web-server/repo/server_https.hpp +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef SERVER_HTTPS_HPP -#define SERVER_HTTPS_HPP - -#include "server_http.hpp" -#include -#include -#include - -namespace SimpleWeb { - typedef boost::asio::ssl::stream HTTPS; - - template<> - class Server : public ServerBase { - std::string session_id_context; - bool set_session_id_context=false; - public: - DEPRECATED Server(unsigned short port, size_t thread_pool_size, const std::string& cert_file, const std::string& private_key_file, - long timeout_request=5, long timeout_content=300, - const std::string& verify_file=std::string()) : - Server(cert_file, private_key_file, verify_file) { - config.port=port; - config.thread_pool_size=thread_pool_size; - config.timeout_request=timeout_request; - config.timeout_content=timeout_content; - } - - Server(const std::string& cert_file, const std::string& private_key_file, const std::string& verify_file=std::string()): - ServerBase::ServerBase(443), context(boost::asio::ssl::context::tlsv12) { - context.use_certificate_chain_file(cert_file); - context.use_private_key_file(private_key_file, boost::asio::ssl::context::pem); - - if(verify_file.size()>0) { - context.load_verify_file(verify_file); - context.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert | - boost::asio::ssl::verify_client_once); - set_session_id_context=true; - } - } - - void start() { - if(set_session_id_context) { - // Creating session_id_context from address:port but reversed due to small SSL_MAX_SSL_SESSION_ID_LENGTH - session_id_context=std::to_string(config.port)+':'; - session_id_context.append(config.address.rbegin(), config.address.rend()); - SSL_CTX_set_session_id_context(context.native_handle(), reinterpret_cast(session_id_context.data()), - std::min(session_id_context.size(), SSL_MAX_SSL_SESSION_ID_LENGTH)); - } - ServerBase::start(); - } - - protected: - boost::asio::ssl::context context; - - void accept() { - //Create new socket for this connection - //Shared_ptr is used to pass temporary objects to the asynchronous functions - auto socket=std::make_shared(*io_service, context); - - acceptor->async_accept((*socket).lowest_layer(), [this, socket](const boost::system::error_code& ec) { - //Immediately start accepting a new connection (if io_service hasn't been stopped) - if (ec != boost::asio::error::operation_aborted) - accept(); - - - if(!ec) { - boost::asio::ip::tcp::no_delay option(true); - socket->lowest_layer().set_option(option); - - //Set timeout on the following boost::asio::ssl::stream::async_handshake - auto timer=get_timeout_timer(socket, config.timeout_request); - socket->async_handshake(boost::asio::ssl::stream_base::server, [this, socket, timer] - (const boost::system::error_code& ec) { - if(timer) - timer->cancel(); - if(!ec) - read_request_and_content(socket); - else if(on_error) - on_error(std::shared_ptr(new Request(*socket)), ec); - }); - } - else if(on_error) - on_error(std::shared_ptr(new Request(*socket)), ec); - }); - } - }; -} - - -#endif /* SERVER_HTTPS_HPP */ - diff --git a/third_party/Simple-web-server/repo/tests/CMakeLists.txt b/third_party/Simple-web-server/repo/tests/CMakeLists.txt deleted file mode 100644 index 6c7034e26c1..00000000000 --- a/third_party/Simple-web-server/repo/tests/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-access-control") - -add_executable(io_test io_test.cpp) -target_link_libraries(io_test ${Boost_LIBRARIES}) -target_link_libraries(io_test ${CMAKE_THREAD_LIBS_INIT}) - -add_executable(parse_test parse_test.cpp) -target_link_libraries(parse_test ${Boost_LIBRARIES}) -target_link_libraries(parse_test ${CMAKE_THREAD_LIBS_INIT}) - -if(MSYS) #TODO: Is MSYS true when MSVC is true? - target_link_libraries(io_test ws2_32 wsock32) - target_link_libraries(parse_test ws2_32 wsock32) -endif() - -add_test(io_test io_test) -add_test(parse_test parse_test) - -if(OPENSSL_FOUND) - add_executable(crypto_test crypto_test.cpp) - target_link_libraries(crypto_test ${OPENSSL_CRYPTO_LIBRARY}) - add_test(crypto_test crypto_test) -endif() diff --git a/third_party/Simple-web-server/repo/tests/crypto_test.cpp b/third_party/Simple-web-server/repo/tests/crypto_test.cpp deleted file mode 100644 index 26b8ce52434..00000000000 --- a/third_party/Simple-web-server/repo/tests/crypto_test.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include -#include - -#include "crypto.hpp" - -using namespace std; -using namespace SimpleWeb; - -const vector > base64_string_tests = { - {"", ""}, - {"f" , "Zg=="}, - {"fo", "Zm8="}, - {"foo", "Zm9v"}, - {"foob", "Zm9vYg=="}, - {"fooba", "Zm9vYmE="}, - {"foobar", "Zm9vYmFy"} -}; - -const vector > md5_string_tests = { - {"", "d41d8cd98f00b204e9800998ecf8427e"}, - {"The quick brown fox jumps over the lazy dog", "9e107d9d372bb6826bd81d3542a419d6"} -}; - -const vector > sha1_string_tests = { - {"", "da39a3ee5e6b4b0d3255bfef95601890afd80709"}, - {"The quick brown fox jumps over the lazy dog", "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"} -}; - -const vector > sha256_string_tests = { - {"", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, - {"The quick brown fox jumps over the lazy dog", "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"} -}; - -const vector > sha512_string_tests = { - {"", "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"}, - {"The quick brown fox jumps over the lazy dog", "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6"} -}; - -int main() { - for(auto& string_test: base64_string_tests) { - assert(Crypto::Base64::encode(string_test.first)==string_test.second); - assert(Crypto::Base64::decode(string_test.second)==string_test.first); - } - - for(auto& string_test: md5_string_tests) { - assert(Crypto::to_hex_string(Crypto::md5(string_test.first)) == string_test.second); - stringstream ss(string_test.first); - assert(Crypto::to_hex_string(Crypto::md5(ss)) == string_test.second); - } - - for(auto& string_test: sha1_string_tests) { - assert(Crypto::to_hex_string(Crypto::sha1(string_test.first)) == string_test.second); - stringstream ss(string_test.first); - assert(Crypto::to_hex_string(Crypto::sha1(ss)) == string_test.second); - } - - for(auto& string_test: sha256_string_tests) { - assert(Crypto::to_hex_string(Crypto::sha256(string_test.first)) == string_test.second); - stringstream ss(string_test.first); - assert(Crypto::to_hex_string(Crypto::sha256(ss)) == string_test.second); - } - - for(auto& string_test: sha512_string_tests) { - assert(Crypto::to_hex_string(Crypto::sha512(string_test.first)) == string_test.second); - stringstream ss(string_test.first); - assert(Crypto::to_hex_string(Crypto::sha512(ss)) == string_test.second); - } - - //Testing iterations - assert(Crypto::to_hex_string(Crypto::sha1("Test", 1)) == "640ab2bae07bedc4c163f679a746f7ab7fb5d1fa"); - assert(Crypto::to_hex_string(Crypto::sha1("Test", 2)) == "af31c6cbdecd88726d0a9b3798c71ef41f1624d5"); - stringstream ss("Test"); - assert(Crypto::to_hex_string(Crypto::sha1(ss, 2)) == "af31c6cbdecd88726d0a9b3798c71ef41f1624d5"); - - assert(Crypto::to_hex_string(Crypto::pbkdf2("Password", "Salt", 4096, 128 / 8)) == "f66df50f8aaa11e4d9721e1312ff2e66"); - assert(Crypto::to_hex_string(Crypto::pbkdf2("Password", "Salt", 8192, 512 / 8)) == "a941ccbc34d1ee8ebbd1d34824a419c3dc4eac9cbc7c36ae6c7ca8725e2b618a6ad22241e787af937b0960cf85aa8ea3a258f243e05d3cc9b08af5dd93be046c"); -} diff --git a/third_party/Simple-web-server/repo/tests/io_test.cpp b/third_party/Simple-web-server/repo/tests/io_test.cpp deleted file mode 100644 index db999cc7468..00000000000 --- a/third_party/Simple-web-server/repo/tests/io_test.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "server_http.hpp" -#include "client_http.hpp" - -#include - -using namespace std; - -typedef SimpleWeb::Server HttpServer; -typedef SimpleWeb::Client HttpClient; - -int main() { - HttpServer server; - server.config.port=8080; - - server.resource["^/string$"]["POST"]=[](shared_ptr response, shared_ptr request) { - auto content=request->content.string(); - - *response << "HTTP/1.1 200 OK\r\nContent-Length: " << content.length() << "\r\n\r\n" << content; - }; - - server.resource["^/info$"]["GET"]=[](shared_ptr response, shared_ptr request) { - stringstream content_stream; - content_stream << request->method << " " << request->path << " " << request->http_version << " "; - content_stream << request->header.find("test parameter")->second; - - content_stream.seekp(0, ios::end); - - *response << "HTTP/1.1 200 OK\r\nContent-Length: " << content_stream.tellp() << "\r\n\r\n" << content_stream.rdbuf(); - }; - - server.resource["^/match/([0-9]+)$"]["GET"]=[&server](shared_ptr response, shared_ptr request) { - string number=request->path_match[1]; - *response << "HTTP/1.1 200 OK\r\nContent-Length: " << number.length() << "\r\n\r\n" << number; - }; - - thread server_thread([&server](){ - //Start server - server.start(); - }); - - this_thread::sleep_for(chrono::seconds(1)); - { - HttpClient client("localhost:8080"); - - { - stringstream output; - auto r=client.request("POST", "/string", "A string"); - output << r->content.rdbuf(); - assert(output.str()=="A string"); - } - - { - stringstream output; - stringstream content("A string"); - auto r=client.request("POST", "/string", content); - output << r->content.rdbuf(); - assert(output.str()=="A string"); - } - - { - stringstream output; - auto r=client.request("GET", "/info", "", {{"Test Parameter", "test value"}}); - output << r->content.rdbuf(); - assert(output.str()=="GET /info 1.1 test value"); - } - - { - stringstream output; - auto r=client.request("GET", "/match/123"); - output << r->content.rdbuf(); - assert(output.str()=="123"); - } - } - { - HttpClient client("localhost:8080"); - - // test performing the stream version of the request methods first - { - stringstream output; - stringstream content("A string"); - auto r=client.request("POST", "/string", content); - output << r->content.rdbuf(); - assert(output.str()=="A string"); - } - - { - stringstream output; - auto r=client.request("POST", "/string", "A string"); - output << r->content.rdbuf(); - assert(output.str()=="A string"); - } - } - - server.stop(); - server_thread.join(); - - return 0; -} diff --git a/third_party/Simple-web-server/repo/tests/parse_test.cpp b/third_party/Simple-web-server/repo/tests/parse_test.cpp deleted file mode 100644 index 785d305a1ea..00000000000 --- a/third_party/Simple-web-server/repo/tests/parse_test.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include "server_http.hpp" -#include "client_http.hpp" -#include -#include - -using namespace std; -using namespace SimpleWeb; - -class ServerTest : public ServerBase { -public: - ServerTest() : ServerBase::ServerBase(8080) {} - - void accept() {} - - void parse_request_test() { - HTTP socket(*io_service); - std::shared_ptr request(new Request(socket)); - - std::ostream stream(&request->content.streambuf); - stream << "GET /test/ HTTP/1.1\r\n"; - stream << "TestHeader: test\r\n"; - stream << "TestHeader2:test2\r\n"; - stream << "TestHeader3:test3a\r\n"; - stream << "TestHeader3:test3b\r\n"; - stream << "\r\n"; - - assert(parse_request(request)); - - assert(request->method=="GET"); - assert(request->path=="/test/"); - assert(request->http_version=="1.1"); - - assert(request->header.size()==4); - auto header_it=request->header.find("TestHeader"); - assert(header_it!=request->header.end() && header_it->second=="test"); - header_it=request->header.find("TestHeader2"); - assert(header_it!=request->header.end() && header_it->second=="test2"); - - header_it=request->header.find("testheader"); - assert(header_it!=request->header.end() && header_it->second=="test"); - header_it=request->header.find("testheader2"); - assert(header_it!=request->header.end() && header_it->second=="test2"); - - auto range=request->header.equal_range("testheader3"); - auto first=range.first; - auto second=first; - ++second; - assert(range.first!=request->header.end() && range.second!=request->header.end() && - ((first->second=="test3a" && second->second=="test3b") || - (first->second=="test3b" && second->second=="test3a"))); - } -}; - -class ClientTest : public ClientBase { -public: - ClientTest(const std::string& server_port_path) : ClientBase::ClientBase(server_port_path, 80) {} - - void connect() {} - - void constructor_parse_test1() { - assert(host=="test.org"); - assert(port==8080); - } - - void constructor_parse_test2() { - assert(host=="test.org"); - assert(port==80); - } - - void parse_response_header_test() { - std::shared_ptr response(new Response()); - - ostream stream(&response->content_buffer); - stream << "HTTP/1.1 200 OK\r\n"; - stream << "TestHeader: test\r\n"; - stream << "TestHeader2:test2\r\n"; - stream << "TestHeader3:test3a\r\n"; - stream << "TestHeader3:test3b\r\n"; - stream << "\r\n"; - - parse_response_header(response); - - assert(response->http_version=="1.1"); - assert(response->status_code=="200 OK"); - - assert(response->header.size()==4); - auto header_it=response->header.find("TestHeader"); - assert(header_it!=response->header.end() && header_it->second=="test"); - header_it=response->header.find("TestHeader2"); - assert(header_it!=response->header.end() && header_it->second=="test2"); - - header_it=response->header.find("testheader"); - assert(header_it!=response->header.end() && header_it->second=="test"); - header_it=response->header.find("testheader2"); - assert(header_it!=response->header.end() && header_it->second=="test2"); - - auto range=response->header.equal_range("testheader3"); - auto first=range.first; - auto second=first; - ++second; - assert(range.first!=response->header.end() && range.second!=response->header.end() && - ((first->second=="test3a" && second->second=="test3b") || - (first->second=="test3b" && second->second=="test3a"))); - } -}; - -int main() { - ServerTest serverTest; - serverTest.io_service=std::make_shared(); - - serverTest.parse_request_test(); - - ClientTest clientTest("test.org:8080"); - clientTest.constructor_parse_test1(); - - ClientTest clientTest2("test.org"); - clientTest2.constructor_parse_test2(); - - clientTest2.parse_response_header_test(); -} diff --git a/third_party/Simple-web-server/repo/web/index.html b/third_party/Simple-web-server/repo/web/index.html deleted file mode 100644 index 3cf66e4ca82..00000000000 --- a/third_party/Simple-web-server/repo/web/index.html +++ /dev/null @@ -1,8 +0,0 @@ - - - Simple-Web-Server html-file - - - This is the content of index.html - - diff --git a/third_party/Simple-web-server/repo/web/test.html b/third_party/Simple-web-server/repo/web/test.html deleted file mode 100644 index af5fe1cc9ed..00000000000 --- a/third_party/Simple-web-server/repo/web/test.html +++ /dev/null @@ -1,8 +0,0 @@ - - - Simple-Web-Server html-file - - - This is the content of test.html - - diff --git a/third_party/angular-material/CMakeLists.txt b/third_party/angular-material/CMakeLists.txt index a772e21457c..ae962f82c2a 100644 --- a/third_party/angular-material/CMakeLists.txt +++ b/third_party/angular-material/CMakeLists.txt @@ -26,9 +26,12 @@ # POSSIBILITY OF SUCH DAMAGE. # +install(FILES repo/angular-material.min.js + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ + DESTINATION /var/otbr/frontend/res/js +) -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/repo/angular-material.min.js - DESTINATION ${OTBR_WEB_DATADIR}/frontend/res/js) - -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/repo/angular-material.min.css - DESTINATION ${OTBR_WEB_DATADIR}/frontend/res/css) +install(FILES repo/angular-material.min.css + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ + DESTINATION /var/otbr/frontend/res/css +) diff --git a/third_party/angular/CMakeLists.txt b/third_party/angular/CMakeLists.txt index 00317eb5359..c098493f1b0 100644 --- a/third_party/angular/CMakeLists.txt +++ b/third_party/angular/CMakeLists.txt @@ -27,8 +27,10 @@ # install(FILES - ${CMAKE_CURRENT_SOURCE_DIR}/repo/angular.min.js - ${CMAKE_CURRENT_SOURCE_DIR}/repo/angular-animate.min.js - ${CMAKE_CURRENT_SOURCE_DIR}/repo/angular-aria.min.js - ${CMAKE_CURRENT_SOURCE_DIR}/repo/angular-messages.min.js - DESTINATION ${OTBR_WEB_DATADIR}/frontend/res/js) + repo/angular.min.js + repo/angular-animate.min.js + repo/angular-aria.min.js + repo/angular-messages.min.js + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ + DESTINATION /var/otbr/frontend/res/js +) diff --git a/third_party/cJSON/CMakeLists.txt b/third_party/cJSON/CMakeLists.txt index 8973dee1e95..80aac7c239e 100644 --- a/third_party/cJSON/CMakeLists.txt +++ b/third_party/cJSON/CMakeLists.txt @@ -39,5 +39,3 @@ target_include_directories(cjson $ $ ) - - diff --git a/third_party/d3js/CMakeLists.txt b/third_party/d3js/CMakeLists.txt index 28d1701602c..407c831d660 100644 --- a/third_party/d3js/CMakeLists.txt +++ b/third_party/d3js/CMakeLists.txt @@ -27,6 +27,8 @@ # install(FILES - ${CMAKE_CURRENT_SOURCE_DIR}/repo/d3.js - ${CMAKE_CURRENT_SOURCE_DIR}/repo/d3.min.js - DESTINATION ${OTBR_WEB_DATADIR}/frontend/res/js) + repo/d3.js + repo/d3.min.js + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ + DESTINATION /var/otbr/frontend/res/js +) diff --git a/third_party/mdl/CMakeLists.txt b/third_party/mdl/CMakeLists.txt index 41646616654..f353f90579a 100644 --- a/third_party/mdl/CMakeLists.txt +++ b/third_party/mdl/CMakeLists.txt @@ -26,9 +26,16 @@ # POSSIBILITY OF SUCH DAMAGE. # +install(FILES + repo/material.min.js + repo/material.min.js.map + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ + DESTINATION /var/otbr/frontend/res/js +) -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/repo/material.min.js - DESTINATION ${OTBR_WEB_DATADIR}/frontend/res/js) - -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/repo/material.min.css - DESTINATION ${OTBR_WEB_DATADIR}/frontend/res/css) +install(FILES + repo/material.min.css + repo/material.min.css.map + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ + DESTINATION /var/otbr/frontend/res/css +)