First commit: http-server 85/5485/1
authorMauro Sardara <[email protected]>
Wed, 22 Feb 2017 22:24:21 +0000 (23:24 +0100)
committerMauro Sardara <[email protected]>
Wed, 22 Feb 2017 22:24:21 +0000 (23:24 +0100)
Change-Id: Ia527fe3065016404b0ea752ddc9e15c96288ed86
Signed-off-by: Mauro Sardara <[email protected]>
25 files changed:
AUTHORS [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
README.md [new file with mode: 0644]
cmake/Modules/FindLibicnet.cmake [new file with mode: 0644]
cmake/Modules/version.cmake [new file with mode: 0644]
http-server/common.h [new file with mode: 0644]
http-server/configuration.cc [new file with mode: 0644]
http-server/configuration.h [new file with mode: 0644]
http-server/content.cc [new file with mode: 0644]
http-server/content.h [new file with mode: 0644]
http-server/http_server.cc [new file with mode: 0644]
http-server/http_server.h [new file with mode: 0644]
http-server/icn_request.cc [new file with mode: 0644]
http-server/icn_request.h [new file with mode: 0644]
http-server/icn_response.cc [new file with mode: 0644]
http-server/icn_response.h [new file with mode: 0644]
http-server/request.cc [new file with mode: 0644]
http-server/request.h [new file with mode: 0644]
http-server/response.cc [new file with mode: 0644]
http-server/response.h [new file with mode: 0644]
http-server/socket_request.cc [new file with mode: 0644]
http-server/socket_request.h [new file with mode: 0644]
http-server/socket_response.cc [new file with mode: 0644]
http-server/socket_response.h [new file with mode: 0644]
main.cc [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..38bc060
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,6 @@
+icnet authors are listed below
+
+    Mauro Sardara <[email protected]>
+    Ole Christian Eidheim (Origin Project)
+
+Copyright (c) 2016-2017 Cisco and/or its affiliates.
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..16c9891
--- /dev/null
@@ -0,0 +1,53 @@
+# Copyright (c) 2017 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+cmake_minimum_required(VERSION 3.2)
+project(http-server)
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O3")
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
+
+find_package(Libicnet REQUIRED)
+include_directories(${LIBICNET_INCLUDE_DIRS})
+
+find_package(Threads REQUIRED)
+
+find_package(Boost 1.53.0 COMPONENTS regex system thread filesystem date_time REQUIRED)
+include_directories(SYSTEM ${Boost_INCLUDE_DIR} ${LIBICNET_INCLUDE_DIR})
+
+set(SOURCE_FILES
+        main.cc
+        http-server/http_server.cc
+        http-server/http_server.h
+        http-server/response.cc
+        http-server/response.h
+        http-server/common.h
+        http-server/socket_response.cc
+        http-server/socket_response.h
+        http-server/icn_response.cc
+        http-server/icn_response.h
+        http-server/content.cc
+        http-server/content.h
+        http-server/request.cc
+        http-server/request.h
+        http-server/icn_request.cc
+        http-server/icn_request.h
+        http-server/socket_request.cc
+        http-server/socket_request.h
+        http-server/configuration.cc
+        http-server/configuration.h)
+
+add_executable(http-server ${SOURCE_FILES})
+target_link_libraries(http-server ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${LIBICNET_LIBRARY})
+
+install(TARGETS http-server DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..a317c80
--- /dev/null
+++ b/README.md
@@ -0,0 +1,76 @@
+HTTP Server over TCP/ICN
+====================================================
+This is an implementation of a HTTP server able to serve client requests
+using both TCP and ICN as transport protocol.
+
+This project is a fork from the http server implemented by Ole Christian Eidheim and
+open sourced at https://github.com/eidheim/Simple-Web-Server.
+
+In the ICN flavour, so far, we support just the GET method. Later we'll be implementing the
+remaining methods as well.
+
+Dependencies
+------------
+
+- libboost-regex-dev
+- libboost-system-dev
+- libboost-filesystem-dev
+- libicnet
+
+Build the HTTP-Server
+-----------------
+
+For building the library, from the root folder of the project:
+```bash
+ $ mkdir build && cd build 
+ $ cmake ..
+ $ make
+```
+
+Install the HTTP-Server
+-------------------
+
+For installing the application:
+
+```bash
+ $ cd build
+ $ sudo make install
+```
+
+Usage
+-----
+
+For starting the http-server, from the build folder:
+
+```bash
+ $ cd build
+ $ ./http-server
+```
+
+The server now is:
+- serving files from the folder **/var/www/html**
+- Listening on the icn name /webserver
+- Listening on the TCP port 8080
+
+For retrieving a content through icn, the name must have the following format:
+
+`iget http://webserver/get/file.mp4`
+
+The server accept two option through the command line:
+
+```bash
+ $ ./http-server -h
+ http-server [-p PATH_TO_ROOT_FOOT_FOLDER] [-l WEBSERVER_PREFIX]
+```
+
+The default values are **/vaw/www/html** for the root folder and **ccnx:/webserver** for the icn name. 
+
+Platforms
+---------
+
+Libicnet has been tested in:
+
+    - Ubuntu 16.04 (x86_64)
+    - Debian Testing
+    - MacOSX 10.12
\ No newline at end of file
diff --git a/cmake/Modules/FindLibicnet.cmake b/cmake/Modules/FindLibicnet.cmake
new file mode 100644 (file)
index 0000000..3c9c4f5
--- /dev/null
@@ -0,0 +1,39 @@
+########################################
+#
+# Find the Libparc libraries and includes
+# This module sets:
+#  LIBICNET_FOUND: True if Libconsumer-producer was found
+#  LIBICNETR_LIBRARY:  The Libconsumer-producer library
+#  LIBICNET_LIBRARIES:  The Libconsumer-producer library and dependencies
+#  LIBICNET_INCLUDE_DIR:  The Libconsumer-producer include dir
+#
+
+set(LIBICNET_SEARCH_PATH_LIST
+        ${LIBICNET_HOME}
+        $ENV{LIBICNETHOME}
+        $ENV{CCNX_HOME}
+        $ENV{PARC_HOME}
+        $ENV{FOUNDATION_HOME}
+        /usr/local/parc
+        /usr/local/ccnx
+        /usr/local/ccn
+        /usr/local
+        /opt
+        /usr
+        )
+
+find_path(LIBICNET_INCLUDE_DIR icnet/icnet_common.h
+        HINTS ${LIBICNET_SEARCH_PATH_LIST}
+        PATH_SUFFIXES include
+        DOC "Find the libicnet includes")
+
+find_library(LIBICNET_LIBRARY NAMES icnet
+        HINTS ${LIBICNET_SEARCH_PATH_LIST}
+        PATH_SUFFIXES lib
+        DOC "Find the libicnet libraries")
+
+set(LIBICNET_LIBRARIES ${LIBICNET_LIBRARY})
+set(LIBICNET_INCLUDE_DIRS ${LIBICNET_INCLUDE_DIR})
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Libicnet DEFAULT_MSG LIBICNET_LIBRARY LIBICNET_INCLUDE_DIR)
diff --git a/cmake/Modules/version.cmake b/cmake/Modules/version.cmake
new file mode 100644 (file)
index 0000000..44a4109
--- /dev/null
@@ -0,0 +1,15 @@
+#
+# Get a version to pass on the command line
+#
+execute_process(COMMAND ${PROJECT_SOURCE_DIR}/cmake/get_version.sh ${PROJECT_SOURCE_DIR}
+        OUTPUT_VARIABLE RELEASE_VERSION
+        OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+execute_process(COMMAND date -u +%Y-%m-%dT%H:%M:%SZ
+        OUTPUT_VARIABLE ISO_DATE
+        OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+MESSAGE(STATUS "Configuring version ${RELEASE_VERSION}")
+
+add_definitions("-DRELEASE_VERSION=\"${RELEASE_VERSION}\"")
+
diff --git a/http-server/common.h b/http-server/common.h
new file mode 100644 (file)
index 0000000..e69706e
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ICN_WEB_SERVER_COMMON_H_
+#define ICN_WEB_SERVER_COMMON_H_
+
+#include <boost/asio.hpp>
+#include <boost/regex.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/functional/hash.hpp>
+#include <memory>
+#include <algorithm>
+
+#include <unordered_map>
+#include <thread>
+#include <future>
+#include <functional>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+// ICN extensions
+#include <icnet/icnet_socket_producer.h>
+
+typedef boost::asio::ip::tcp::socket socket_type;
+typedef std::function<void(const boost::system::error_code &)> SendCallback;
+
+#endif // ICN_WEB_SERVER_COMMON_H_
diff --git a/http-server/configuration.cc b/http-server/configuration.cc
new file mode 100644 (file)
index 0000000..8f205a0
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "configuration.h"
+
+namespace icn_httpserver {
+
+Configuration::Configuration(unsigned short port, size_t num_threads)
+    : num_threads_(num_threads), port_(port), reuse_address_(true) {
+}
+
+size_t Configuration::getNum_threads() const {
+  return num_threads_;
+}
+
+void Configuration::setNum_threads(size_t num_threads) {
+  Configuration::num_threads_ = num_threads;
+}
+
+unsigned short Configuration::getPort() const {
+  return port_;
+}
+
+void Configuration::setPort(unsigned short port) {
+  Configuration::port_ = port;
+}
+
+const std::string &Configuration::getAddress() const {
+  return address_;
+}
+
+void Configuration::setAddress(const std::string &address) {
+  Configuration::address_ = address;
+}
+
+bool Configuration::isReuse_address() const {
+  return reuse_address_;
+}
+
+void Configuration::setReuse_address(bool reuse_address) {
+  Configuration::reuse_address_ = reuse_address;
+}
+
+} // end namespace icn_httpserver
\ No newline at end of file
diff --git a/http-server/configuration.h b/http-server/configuration.h
new file mode 100644 (file)
index 0000000..65d3170
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ICN_WEB_SERVER_CONFIGURATION_H_
+#define ICN_WEB_SERVER_CONFIGURATION_H_
+
+#include "common.h"
+
+namespace icn_httpserver {
+
+class Configuration {
+ public:
+  Configuration(unsigned short port, size_t num_threads);
+
+  size_t getNum_threads() const;
+
+  void setNum_threads(size_t num_threads);
+
+  unsigned short getPort() const;
+
+  void setPort(unsigned short port);
+
+  const std::string &getAddress() const;
+
+  void setAddress(const std::string &address);
+
+  bool isReuse_address() const;
+
+  void setReuse_address(bool reuse_address);
+
+ private:
+  size_t num_threads_;
+  unsigned short port_;
+  std::string address_;
+  bool reuse_address_;
+};
+
+} // end namespace icn_httpserver
+
+#endif // ICN_WEB_SERVER_CONFIGURATION_H_
diff --git a/http-server/content.cc b/http-server/content.cc
new file mode 100644 (file)
index 0000000..f6bd048
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "content.h"
+
+namespace icn_httpserver {
+
+Content::Content(boost::asio::streambuf &streambuf)
+    : std::istream(&streambuf), streambuf_(streambuf) {
+}
+
+std::size_t Content::size() {
+  return streambuf_.size();
+}
+
+std::string Content::string() {
+  std::stringstream ss;
+  ss << rdbuf();
+  return ss.str();
+}
+
+} // end namespace icn_httpserver
\ No newline at end of file
diff --git a/http-server/content.h b/http-server/content.h
new file mode 100644 (file)
index 0000000..a81ad64
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common.h"
+
+#ifndef ICN_WEB_SERVER_CONTENT_H_
+#define ICN_WEB_SERVER_CONTENT_H_
+
+namespace icn_httpserver {
+
+class Content
+    : public std::istream {
+ public:
+  Content(boost::asio::streambuf &streambuf);
+
+  size_t size();
+
+  std::string string();
+
+ private:
+  boost::asio::streambuf &streambuf_;
+};
+
+} // end namespace icn_httpserver
+
+#endif // ICN_WEB_SERVER_CONTENT_H_
diff --git a/http-server/http_server.cc b/http-server/http_server.cc
new file mode 100644 (file)
index 0000000..ebc8503
--- /dev/null
@@ -0,0 +1,487 @@
+/*
+ * 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.
+ */
+
+#include "http_server.h"
+
+namespace icn_httpserver {
+
+HttpServer::HttpServer(unsigned short port,
+                       std::string icn_name, size_t num_threads, long timeout_request, long timeout_send_or_receive)
+    : config_(port, num_threads),
+      icn_name_(icn_name),
+      internal_io_service_(std::make_shared<boost::asio::io_service>()),
+      io_service_(*internal_io_service_),
+      acceptor_(io_service_),
+      acceptor_producer_(std::make_shared<icnet::ProducerSocket>(icnet::Name(icn_name))),
+      timeout_request_(timeout_request),
+      timeout_content_(timeout_send_or_receive) {
+}
+
+HttpServer::HttpServer(unsigned short port,
+                       std::string icn_name,
+                       size_t num_threads,
+                       long timeout_request,
+                       long timeout_send_or_receive,
+                       boost::asio::io_service &ioService)
+    : config_(port, num_threads),
+      icn_name_(icn_name),
+      io_service_(ioService),
+      acceptor_(io_service_),
+      acceptor_producer_(std::make_shared<icnet::ProducerSocket>(icnet::Name(icn_name))),
+      timeout_request_(timeout_request),
+      timeout_content_(timeout_send_or_receive) {
+}
+
+void HttpServer::processIncomingInterest(icnet::ProducerSocket &p, const icnet::Interest &interest) {
+  icnet::Name complete_name = interest.getName();
+
+  if (complete_name.getSegmentCount() <= 2) {
+    std::cerr << "Received malformed name " << complete_name << ". Ignoring it." << std::endl;
+    return;
+  }
+
+  icnet::Name request_name = complete_name.get(-1).isSegment() ? complete_name.getPrefix(-1) : complete_name;
+
+  std::unique_lock<std::mutex> lock(thread_list_mtx_);
+  if (icn_producers_.size() < config_.getNum_threads()) {
+    if (icn_producers_.find(request_name) == icn_producers_.end()) {
+      std::cout << "Received interest name: " << request_name << std::endl;
+      std::shared_ptr<icnet::ProducerSocket> p = makeProducer(request_name);
+      icn_producers_[request_name] = p;
+      std::cout << "Starting new thread" << std::endl;
+      std::thread t([this, interest, request_name, p]() {
+        processInterest(request_name, p);
+      });
+      t.detach();
+    } else {
+      icn_producers_[request_name]->onInterest(complete_name, interest);
+    }
+  }
+}
+
+void HttpServer::signPacket(icnet::ProducerSocket &p, icnet::ContentObject &content_object) {
+  // This is not really signing the packet. Signing every packet is cpu expensive.
+  icnet::KeyLocator keyLocator;
+  content_object.signWithSha256(keyLocator);
+}
+
+void HttpServer::processInterest(icnet::Name request_name, std::shared_ptr<icnet::ProducerSocket> p) {
+  // Create timer
+  std::shared_ptr<icnet::ccnx::Portal> portal;
+  p->getSocketOption(icnet::GeneralTransportOptions::PORTAL, portal);
+  boost::asio::io_service &ioService = portal->getIoService();
+
+  boost::asio::deadline_timer t(ioService, boost::posix_time::seconds(5));
+
+  std::function<void(const boost::system::error_code e)>
+      wait_callback = [&ioService](const boost::system::error_code e) {
+    if (!e) {
+      // Be sure to delete the timer before the io_service, otherwise we'll get some strange behavior!
+      ioService.stop();
+    }
+  };
+
+  t.async_wait(wait_callback);
+
+  // Get the name of the HTTP method to compute
+  std::string method = request_name.get(1).toString();
+  std::transform(method.begin(), method.end(), method.begin(), ::toupper);
+  std::string path;
+
+  // This is done for getting rid of useless name components such as ccnx: or ndn:
+  if (request_name.getSegmentCount() > 2) {
+    std::string rawPath = request_name.getSubName(2).toString();
+    std::size_t pos = rawPath.find("/");
+    path = rawPath.substr(pos);
+  }
+
+  std::function<void(icnet::ProducerSocket &p, const icnet::Interest &interest)>
+      interest_enter_callback = [this, &wait_callback, &t](icnet::ProducerSocket &p, const icnet::Interest &interest) {
+    t.cancel();
+    t.expires_from_now(boost::posix_time::seconds(5));
+    t.async_wait(wait_callback);
+  };
+
+  p->setSocketOption(icnet::ProducerCallbacksOptions::INTEREST_INPUT,
+                     (icnet::ProducerInterestCallback) interest_enter_callback);
+
+  // TODO The parsing of the parameters in theURL is missing!
+  if (method == GET) {
+    // Build new GET request to submit to the server
+
+    std::shared_ptr<Request> request = std::make_shared<IcnRequest>(p, request_name.toString(), path, method, "1.0");
+
+    std::static_pointer_cast<IcnRequest>(request)->getHeader()
+        .insert(std::make_pair(std::string("Host"), std::string("localhost")));
+
+    p->attach();
+
+    find_resource(nullptr, request);
+  }
+
+  p->serveForever();
+
+  std::unique_lock<std::mutex> lock(thread_list_mtx_);
+  icn_producers_.erase(request_name);
+}
+
+std::shared_ptr<icnet::ProducerSocket> HttpServer::makeProducer(icnet::Name request_name) {
+  std::shared_ptr<icnet::ProducerSocket> producer = std::make_shared<icnet::ProducerSocket>(request_name);
+  //  producer->setContextOption(FAST_SIGNING, true);
+  //  producer->setContextOption(DATA_TO_SECURE, (api::ProducerDataCallback) bind(&http-server::signPacket, this, _1, _2));
+  producer->setSocketOption(icnet::GeneralTransportOptions::DATA_PACKET_SIZE, PACKET_SIZE);
+  producer->setSocketOption(icnet::GeneralTransportOptions::OUTPUT_BUFFER_SIZE, SEND_BUFFER_SIZE);
+
+  return producer;
+}
+
+void HttpServer::setIcnAcceptor() {
+  acceptor_producer_->setSocketOption(icnet::ProducerCallbacksOptions::INTEREST_INPUT,
+                                      (icnet::ProducerInterestCallback) bind(&HttpServer::processIncomingInterest,
+                                                                             this,
+                                                                             std::placeholders::_1,
+                                                                             std::placeholders::_2));
+  acceptor_producer_->dispatch();
+}
+
+void HttpServer::spawnTcpThreads() {
+  if (io_service_.stopped()) {
+    io_service_.reset();
+  }
+
+  boost::asio::ip::tcp::endpoint endpoint;
+
+  if (config_.getAddress().size() > 0) {
+    endpoint =
+        boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(config_.getAddress()), config_.getPort());
+  } else {
+    endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), config_.getPort());
+  }
+
+  acceptor_.open(endpoint.protocol());
+  acceptor_.set_option(boost::asio::socket_base::reuse_address(config_.isReuse_address()));
+  acceptor_.bind(endpoint);
+  acceptor_.listen();
+
+  accept();
+
+  //If num_threads>1, start m_io_service.run() in (num_threads-1) threads for thread-pooling
+  socket_threads_.clear();
+  for (size_t c = 1; c < config_.getNum_threads(); c++) {
+    socket_threads_.emplace_back([this]() {
+      io_service_.run();
+    });
+  }
+}
+
+void HttpServer::start() {
+  //Copy the resources to opt_resource for more efficient request processing
+  opt_resource_.clear();
+  for (auto &res: resource) {
+    for (auto &res_method: res.second) {
+      auto it = opt_resource_.end();
+      for (auto opt_it = opt_resource_.begin(); opt_it != opt_resource_.end(); opt_it++) {
+        if (res_method.first == opt_it->first) {
+          it = opt_it;
+          break;
+        }
+      }
+      if (it == opt_resource_.end()) {
+        opt_resource_.emplace_back();
+        it = opt_resource_.begin() + (opt_resource_.size() - 1);
+        it->first = res_method.first;
+      }
+      it->second.emplace_back(boost::regex(res.first), res_method.second);
+    }
+  }
+
+  spawnTcpThreads();
+  setIcnAcceptor();
+
+
+
+  // Wait for the rest of the threads, if any, to finish as well
+  for (auto &t: socket_threads_) {
+    t.join();
+  }
+
+  //  for (auto &t : icn_threads) {
+  //    t.second.get();
+  //  }
+}
+
+void HttpServer::stop() {
+  acceptor_.close();
+  acceptor_producer_.reset();
+  io_service_.stop();
+
+  for (auto p: icn_producers_) {
+    std::shared_ptr<icnet::ccnx::Portal> portalPtr;
+    p.second->getSocketOption(icnet::GeneralTransportOptions::PORTAL, portalPtr);
+    portalPtr->getIoService().stop();
+  }
+
+  for (auto p : icn_producers_) {
+    p.second.reset();
+  }
+
+}
+
+void HttpServer::accept() {
+  //Create new socket for this connection
+  //Shared_ptr is used to pass temporary objects to the asynchronous functions
+  std::shared_ptr<socket_type> socket = std::make_shared<socket_type>(io_service_);
+
+  acceptor_.async_accept(*socket, [this, socket](const boost::system::error_code &ec) {
+    //Immediately start accepting a new connection
+    accept();
+
+    if (!ec) {
+      boost::asio::ip::tcp::no_delay option(true);
+      socket->set_option(option);
+      read_request_and_content(socket);
+    }
+  });
+}
+
+void HttpServer::send(std::shared_ptr<Response> response, SendCallback callback) const {
+  response->send(callback);
+}
+
+std::shared_ptr<boost::asio::deadline_timer> HttpServer::set_timeout_on_socket(std::shared_ptr<socket_type> socket,
+                                                                               long seconds) {
+  std::shared_ptr<boost::asio::deadline_timer> timer = std::make_shared<boost::asio::deadline_timer>(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 HttpServer::read_request_and_content(std::shared_ptr<socket_type> 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> request = std::make_shared<SocketRequest>();
+  request->read_remote_endpoint_data(*socket);
+
+  //Set timeout on the following boost::asio::async-read or write function
+  std::shared_ptr<boost::asio::deadline_timer> timer;
+  if (timeout_request_ > 0) {
+    timer = set_timeout_on_socket(socket, timeout_request_);
+  }
+
+  boost::asio::async_read_until(*socket,
+                                request->getStreambuf(),
+                                "\r\n\r\n",
+                                [this, socket, request, timer](const boost::system::error_code &ec,
+                                                               size_t bytes_transferred) {
+                                  if (timeout_request_ > 0) {
+                                    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->getStreambuf().size() - bytes_transferred;
+
+                                    if (!parse_request(request, request->getContent())) {
+                                      return;
+                                    }
+
+                                    //If content, read that as well
+                                    auto it = request->getHeader().find("Content-Length");
+                                    if (it != request->getHeader().end()) {
+                                      //Set timeout on the following boost::asio::async-read or write function
+                                      std::shared_ptr<boost::asio::deadline_timer> timer;
+                                      if (timeout_content_ > 0) {
+                                        timer = set_timeout_on_socket(socket, timeout_content_);
+                                      }
+                                      unsigned long long content_length;
+                                      try {
+                                        content_length = stoull(it->second);
+                                      } catch (const std::exception &) {
+                                        return;
+                                      }
+                                      if (content_length > num_additional_bytes) {
+                                        boost::asio::async_read(*socket,
+                                                                request->getStreambuf(),
+                                                                boost::asio::transfer_exactly(
+                                                                    content_length - num_additional_bytes),
+                                                                [this, socket, request, timer](const boost::system::error_code &ec,
+                                                                                               size_t /*bytes_transferred*/) {
+                                                                  if (timeout_content_ > 0) {
+                                                                    timer->cancel();
+                                                                  }
+                                                                  if (!ec) {
+                                                                    find_resource(socket, request);
+                                                                  }
+                                                                });
+                                      } else {
+
+                                        if (timeout_content_ > 0) {
+                                          timer->cancel();
+                                        }
+
+                                        find_resource(socket, request);
+                                      }
+                                    } else {
+                                      find_resource(socket, request);
+                                    }
+                                  }
+                                });
+}
+
+bool HttpServer::parse_request(std::shared_ptr<Request> request, std::istream &stream) const {
+  std::string line;
+  getline(stream, 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->setMethod(line.substr(0, method_end));
+      request->setPath(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.substr(path_end + 1, protocol_end - path_end - 1) != "HTTP") {
+          return false;
+        }
+        request->setHttp_version(line.substr(protocol_end + 1, line.size() - protocol_end - 2));
+      } else {
+        return false;
+      }
+
+      getline(stream, line);
+      size_t param_end;
+      while ((param_end = line.find(':')) != std::string::npos) {
+        size_t value_start = param_end + 1;
+        if ((value_start) < line.size()) {
+          if (line[value_start] == ' ') {
+            value_start++;
+          }
+          if (value_start < line.size()) {
+            request->getHeader().insert(std::make_pair(line.substr(0, param_end),
+                                                       line.substr(value_start, line.size() - value_start - 1)));
+          }
+        }
+
+        getline(stream, line);
+      }
+    } else {
+      return false;
+    }
+  } else {
+    return false;
+  }
+  return true;
+}
+
+void HttpServer::find_resource(std::shared_ptr<socket_type> socket, std::shared_ptr<Request> request) {
+  //Find path- and method-match, and call write_response
+  for (auto &res: opt_resource_) {
+    if (request->getMethod() == res.first) {
+      for (auto &res_path: res.second) {
+        boost::smatch sm_res;
+        if (boost::regex_match(request->getPath(), sm_res, res_path.first)) {
+          request->setPath_match(std::move(sm_res));
+          write_response(socket, request, res_path.second);
+          return;
+        }
+      }
+    }
+  }
+  auto it_method = default_resource.find(request->getMethod());
+  if (it_method != default_resource.end()) {
+    write_response(socket, request, it_method->second);
+    return;
+  }
+
+  std::cout << "resource not found" << std::endl;
+}
+
+void HttpServer::write_response(std::shared_ptr<socket_type> socket,
+                                std::shared_ptr<Request> request,
+                                ResourceCallback &resource_function) {
+  //Set timeout on the following boost::asio::async-read or write function
+  std::shared_ptr<boost::asio::deadline_timer> timer;
+  if (timeout_content_ > 0 && socket) {
+    timer = set_timeout_on_socket(socket, timeout_content_);
+  }
+
+  Response *resp;
+
+  if (socket) {
+    resp = new SocketResponse(socket);
+  } else {
+    resp = new IcnResponse(std::static_pointer_cast<IcnRequest>(request)->getProducer(),
+                           std::static_pointer_cast<IcnRequest>(request)->getName(),
+                           std::static_pointer_cast<IcnRequest>(request)->getPath(),
+                           std::static_pointer_cast<IcnRequest>(request)->getRequest_id());
+  }
+
+  auto response = std::shared_ptr<Response>(resp, [this, request, timer, socket](Response *response_ptr) {
+
+    auto response = std::shared_ptr<Response>(response_ptr);
+    response->setIsLast(true);
+
+    send(response, [this, response, request, timer, socket](const boost::system::error_code &ec) {
+      if (!ec) {
+        if (socket && timeout_content_ > 0) {
+          timer->cancel();
+        }
+
+        float http_version;
+        try {
+          http_version = stof(request->getHttp_version());
+        } catch (const std::exception &) {
+          return;
+        }
+
+        auto range = request->getHeader().equal_range("Connection");
+        for (auto it = range.first; it != range.second; it++) {
+          if (boost::iequals(it->second, "close")) {
+            return;
+          }
+        }
+        if (http_version > 1.05) {
+          read_request_and_content(std::static_pointer_cast<SocketResponse>(response)->getSocket());
+        }
+      }
+    });
+  });
+
+  try {
+    resource_function(response, request);
+  } catch (const std::exception &) {
+    return;
+  }
+}
+
+} // end namespace icn_httpserver
+
diff --git a/http-server/http_server.h b/http-server/http_server.h
new file mode 100644 (file)
index 0000000..fbc841c
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+#ifndef ICN_WEB_SERVER_WEB_SERVER_H_
+#define ICN_WEB_SERVER_WEB_SERVER_H_
+
+#include "common.h"
+#include "icn_request.h"
+#include "icn_response.h"
+#include "socket_request.h"
+#include "socket_response.h"
+#include "configuration.h"
+
+typedef std::function<void(std::shared_ptr<icn_httpserver::Response>, std::shared_ptr<icn_httpserver::Request>)>
+    ResourceCallback;
+
+#define SERVER_NAME "/webserver"
+#define PACKET_SIZE 1500
+#define SEND_BUFFER_SIZE 30000
+
+#define GET "GET"
+#define POST "POST"
+#define PUT "PUT"
+#define DELETE "DELETE"
+#define PATCH "PATCH"
+
+namespace icn_httpserver {
+
+class HttpServer {
+ public:
+  explicit HttpServer(unsigned short port,
+                      std::string icn_name,
+                      size_t num_threads,
+                      long timeout_request,
+                      long timeout_send_or_receive);
+
+  explicit HttpServer(unsigned short port,
+                      std::string icn_name,
+                      size_t num_threads,
+                      long timeout_request,
+                      long timeout_send_or_receive,
+                      boost::asio::io_service &ioService);
+
+  void start();
+
+  void stop();
+
+  void accept();
+
+  void send(std::shared_ptr<Response> response, SendCallback callback = nullptr) const;
+
+  std::unordered_map<std::string, std::unordered_map<std::string, ResourceCallback> > resource;
+  std::unordered_map<std::string, ResourceCallback> default_resource;
+
+ private:
+  void processInterest(icnet::Name request_name, std::shared_ptr<icnet::ProducerSocket> p);
+
+  void processIncomingInterest(icnet::ProducerSocket &p, const icnet::Interest &interest);
+
+  void signPacket(icnet::ProducerSocket &p, icnet::ContentObject &content_object);
+
+  void spawnTcpThreads();
+
+  void setIcnAcceptor();
+
+  std::shared_ptr<boost::asio::deadline_timer> set_timeout_on_socket(std::shared_ptr<socket_type> socket, long seconds);
+
+  void read_request_and_content(std::shared_ptr<socket_type> socket);
+
+  bool parse_request(std::shared_ptr<Request> request, std::istream &stream) const;
+
+  void find_resource(std::shared_ptr<socket_type> socket, std::shared_ptr<Request> request);
+
+  void write_response(std::shared_ptr<socket_type> socket,
+                      std::shared_ptr<Request> request,
+                      ResourceCallback &resource_function);
+
+  std::shared_ptr<icnet::ProducerSocket> makeProducer(icnet::Name request_name);
+
+  Configuration config_;
+
+  std::vector<std::pair<std::string, std::vector<std::pair<boost::regex, ResourceCallback> > > > opt_resource_;
+
+  std::shared_ptr<boost::asio::io_service> internal_io_service_;
+  boost::asio::io_service &io_service_;
+  boost::asio::ip::tcp::acceptor acceptor_;
+  std::vector<std::thread> socket_threads_;
+
+  // ICN parameters
+  std::string icn_name_;
+  std::shared_ptr<icnet::ProducerSocket> acceptor_producer_;
+  std::unordered_map<icnet::Name, std::future<void>> icn_threads_;
+  std::unordered_map<icnet::Name, std::shared_ptr<icnet::ProducerSocket>> icn_producers_;
+  std::unordered_map<icnet::Name, std::shared_ptr<boost::asio::io_service>> name_io_service_map_;
+  std::mutex thread_list_mtx_;
+
+  long timeout_request_;
+  long timeout_content_;
+
+};
+
+} // end namespace icn_httpserver
+
+#endif //ICN_WEB_SERVER_WEB_SERVER_H_
diff --git a/http-server/icn_request.cc b/http-server/icn_request.cc
new file mode 100644 (file)
index 0000000..07d95a2
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "icn_request.h"
+
+namespace icn_httpserver {
+
+IcnRequest::IcnRequest(std::shared_ptr<icnet::ProducerSocket> producer)
+    : producer_(producer) {
+  time_t t;
+  time(&t);
+  srand((unsigned int) t);
+  request_id_ = rand();
+}
+
+IcnRequest::IcnRequest(std::shared_ptr<icnet::ProducerSocket> producer,
+                       std::string name,
+                       std::string path,
+                       std::string method, std::string http_version)
+    : IcnRequest(producer) {
+  this->name_ = name;
+  this->path_ = path;
+  this->method_ = method;
+  this->http_version_ = http_version;
+}
+
+const std::string &IcnRequest::getName() const {
+  return name_;
+}
+
+void IcnRequest::setName(const std::string &name) {
+  IcnRequest::name_ = name;
+}
+
+int IcnRequest::getRequest_id() const {
+  return request_id_;
+}
+
+void IcnRequest::setRequest_id(int request_id) {
+  IcnRequest::request_id_ = request_id;
+}
+
+const std::shared_ptr<icnet::ProducerSocket> &IcnRequest::getProducer() const {
+  return producer_;
+}
+
+void IcnRequest::setProducer(const std::shared_ptr<icnet::ProducerSocket> &producer) {
+  IcnRequest::producer_ = producer;
+}
+
+} // end namespace icn_httpserver
diff --git a/http-server/icn_request.h b/http-server/icn_request.h
new file mode 100644 (file)
index 0000000..c5aa10e
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ICN_WEB_SERVER_ICNREQUEST_H_
+#define ICN_WEB_SERVER_ICNREQUEST_H_
+
+#include "common.h"
+#include "request.h"
+
+namespace icn_httpserver {
+
+class IcnRequest
+    : public Request {
+ public:
+  IcnRequest(std::shared_ptr<icnet::ProducerSocket> producer);
+
+  IcnRequest(std::shared_ptr<icnet::ProducerSocket> producer,
+             std::string name,
+             std::string path,
+             std::string method,
+             std::string http_version);
+
+  const std::string &getName() const;
+
+  void setName(const std::string &name);
+
+  int getRequest_id() const;
+
+  void setRequest_id(int request_id);
+
+  const std::shared_ptr<icnet::ProducerSocket> &getProducer() const;
+
+  void setProducer(const std::shared_ptr<icnet::ProducerSocket> &producer);
+
+ private:
+  std::string name_;
+  int request_id_;
+  std::shared_ptr<icnet::ProducerSocket> producer_;
+
+};
+
+} // end namespace icn_httpserver
+
+#endif // ICN_WEB_SERVER_ICNREQUEST_H_
diff --git a/http-server/icn_response.cc b/http-server/icn_response.cc
new file mode 100644 (file)
index 0000000..6fe8b1e
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "icn_response.h"
+
+namespace icn_httpserver {
+
+IcnResponse::IcnResponse(std::shared_ptr<icnet::ProducerSocket> producer,
+                         std::string ndn_name,
+                         std::string ndn_path,
+                         int response_id)
+    : producer_(producer), ndn_name_(ndn_name), ndn_path_(ndn_path), response_id_(response_id) {
+}
+
+void IcnResponse::send(const SendCallback &callback) {
+  std::size_t buffer_size = this->streambuf_.size();
+  this->streambuf_.commit(this->streambuf_.size());
+
+  this->producer_->produce(icnet::Name(/*this->ndn_name*/),
+                           boost::asio::buffer_cast<const uint8_t *>(this->streambuf_.data()),
+                           buffer_size,
+                           this->response_id_,
+                           this->is_last_);
+
+  this->streambuf_.consume(buffer_size);
+
+  if (callback) {
+    callback(boost::system::error_code());
+  }
+}
+
+} // end namespace icn_httpserver
diff --git a/http-server/icn_response.h b/http-server/icn_response.h
new file mode 100644 (file)
index 0000000..e9af0d4
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "response.h"
+
+#ifndef ICN_WEB_SERVER_ICNRESPONSE_H_
+#define ICN_WEB_SERVER_ICNRESPONSE_H_
+
+namespace icn_httpserver {
+
+class IcnResponse
+    : public Response {
+
+ public:
+
+  IcnResponse(std::shared_ptr<icnet::ProducerSocket> producer,
+              std::string ndn_name,
+              std::string ndn_path,
+              int response_id);
+
+  void send(const SendCallback &callback = nullptr);
+
+ private:
+  std::string ndn_name_;
+  std::string ndn_path_;
+  int response_id_;
+  std::shared_ptr<icnet::ProducerSocket> producer_;
+};
+
+} // end namespace icn_httpserver
+
+#endif // ICN_WEB_SERVER_ICNRESPONSE_H_
diff --git a/http-server/request.cc b/http-server/request.cc
new file mode 100644 (file)
index 0000000..0e726dc
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "request.h"
+
+namespace icn_httpserver {
+
+Request::Request()
+    : content_(streambuf_) {
+}
+
+const std::string &Request::getMethod() const {
+  return method_;
+}
+
+void Request::setMethod(const std::string &method) {
+  Request::method_ = method;
+}
+
+const std::string &Request::getPath() const {
+  return path_;
+}
+
+void Request::setPath(const std::string &path) {
+  Request::path_ = path;
+}
+
+const std::string &Request::getHttp_version() const {
+  return http_version_;
+}
+
+void Request::setHttp_version(const std::string &http_version) {
+  Request::http_version_ = http_version;
+}
+
+std::unordered_multimap<std::string, std::string, ihash, iequal_to> &Request::getHeader() {
+  return header_;
+}
+
+Content &Request::getContent() {
+  return content_;
+}
+
+const boost::smatch &Request::getPath_match() const {
+  return path_match_;
+}
+
+void Request::setPath_match(const boost::smatch &path_match) {
+  Request::path_match_ = path_match;
+}
+
+} // end namespace icn_httpserver
diff --git a/http-server/request.h b/http-server/request.h
new file mode 100644 (file)
index 0000000..6ffff29
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ICN_WEB_SERVER_REQUEST_H_
+#define ICN_WEB_SERVER_REQUEST_H_
+
+#include "common.h"
+#include "content.h"
+
+namespace icn_httpserver {
+
+class iequal_to {
+ public:
+  bool operator()(const std::string &key1, const std::string &key2) const {
+    return boost::algorithm::iequals(key1, key2);
+  }
+};
+
+class ihash {
+ 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;
+  }
+};
+
+class Request {
+ public:
+
+  Request();
+
+  virtual void read_remote_endpoint_data(socket_type &socket) {
+  };
+
+  const std::string &getMethod() const;
+
+  void setMethod(const std::string &method);
+
+  const std::string &getPath() const;
+
+  void setPath(const std::string &path);
+
+  const std::string &getHttp_version() const;
+
+  void setHttp_version(const std::string &http_version);
+
+  std::unordered_multimap<std::string, std::string, ihash, iequal_to> &getHeader();
+
+  boost::asio::streambuf &getStreambuf() {
+    return streambuf_;
+  }
+
+  Content &getContent();
+
+  const boost::smatch &getPath_match() const;
+
+  void setPath_match(const boost::smatch &path_match);
+
+ protected:
+  std::string method_, path_, http_version_;
+  Content content_;
+  std::unordered_multimap<std::string, std::string, ihash, iequal_to> header_;
+  boost::smatch path_match_;
+  boost::asio::streambuf streambuf_;
+};
+
+} // end namespace icn_httpserver
+
+#endif // ICN_WEB_SERVER_REQUEST_H_
diff --git a/http-server/response.cc b/http-server/response.cc
new file mode 100644 (file)
index 0000000..b322cad
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common.h"
+#include "response.h"
+
+namespace icn_httpserver {
+
+Response::Response()
+    : std::ostream(&streambuf_), is_last_(false) {
+}
+
+Response::~Response() {
+}
+
+std::size_t Response::size() {
+  return streambuf_.size();
+}
+
+bool Response::isIsLast() const {
+  return is_last_;
+}
+
+void Response::setIsLast(bool is_last) {
+  Response::is_last_ = is_last;
+}
+
+void Response::setResponseLength(std::size_t length) {
+  response_length_ = length;
+}
+
+} // end namespace icn_httpserver
diff --git a/http-server/response.h b/http-server/response.h
new file mode 100644 (file)
index 0000000..649bcea
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ICN_WEB_SERVER_RESPONSE_H_
+#define ICN_WEB_SERVER_RESPONSE_H_
+
+#include "common.h"
+
+namespace icn_httpserver {
+
+class Response
+    : public std::ostream {
+ public:
+  Response();
+
+  virtual
+  ~Response();
+
+  size_t size();
+
+  virtual void send(const SendCallback &callback = nullptr) {
+  };
+
+  bool isIsLast() const;
+
+  void setIsLast(bool is_last);
+
+  void setResponseLength(std::size_t length);
+
+ protected:
+  boost::asio::streambuf streambuf_;
+  bool is_last_;
+  std::size_t response_length_;
+};
+
+} // end namespace icn_httpserver
+
+#endif // ICN_WEB_SERVER_RESPONSE_H_
diff --git a/http-server/socket_request.cc b/http-server/socket_request.cc
new file mode 100644 (file)
index 0000000..10c663a
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#include "socket_request.h"
+
+namespace icn_httpserver {
+
+void SocketRequest::read_remote_endpoint_data(socket_type &socket) {
+  try {
+    remote_endpoint_address_ = socket.lowest_layer().remote_endpoint().address().to_string();
+    remote_endpoint_port_ = socket.lowest_layer().remote_endpoint().port();
+  } catch (const std::exception &) {
+  }
+}
+
+} // end namespace icn_httpserver
diff --git a/http-server/socket_request.h b/http-server/socket_request.h
new file mode 100644 (file)
index 0000000..cc77ef7
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef ICN_WEB_SERVER_SOCKETREQUEST_H_
+#define ICN_WEB_SERVER_SOCKETREQUEST_H_
+
+#include "request.h"
+
+namespace icn_httpserver {
+
+class SocketRequest
+    : public Request {
+ public:
+  void read_remote_endpoint_data(socket_type &socket);
+
+ private:
+  std::string remote_endpoint_address_;
+  unsigned short remote_endpoint_port_;
+};
+
+} // end namespace icn_httpserver
+
+#endif // ICN_WEB_SERVER_SOCKETREQUEST_H_
diff --git a/http-server/socket_response.cc b/http-server/socket_response.cc
new file mode 100644 (file)
index 0000000..ec33820
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#include "socket_response.h"
+
+namespace icn_httpserver {
+
+SocketResponse::SocketResponse(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
+    : socket_(socket) {
+}
+
+SocketResponse::~SocketResponse() {
+};
+
+void SocketResponse::send(const SendCallback &callback) {
+  boost::asio::async_write(*this->socket_,
+                           this->streambuf_,
+                           [this, callback](const boost::system::error_code &ec, size_t /*bytes_transferred*/) {
+                             if (callback) {
+                               callback(ec);
+                             }
+                           });
+}
+
+const std::shared_ptr<socket_type> &SocketResponse::getSocket() const {
+  return socket_;
+}
+
+void SocketResponse::setSocket(const std::shared_ptr<socket_type> &socket) {
+  SocketResponse::socket_ = socket;
+}
+
+} // end namespace icn_httpserver
diff --git a/http-server/socket_response.h b/http-server/socket_response.h
new file mode 100644 (file)
index 0000000..722e819
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef ICN_WEB_SERVER_SOCKETRESPONSE_H_
+#define ICN_WEB_SERVER_SOCKETRESPONSE_H_
+
+#include "response.h"
+
+namespace icn_httpserver {
+
+class SocketResponse
+    : public Response {
+ public:
+
+  SocketResponse(std::shared_ptr<socket_type> socket);
+
+  ~SocketResponse();
+
+  void send(const SendCallback &callback = nullptr);
+
+  const std::shared_ptr<socket_type> &getSocket() const;
+
+  void setSocket(const std::shared_ptr<socket_type> &socket);
+
+ private:
+  std::shared_ptr<socket_type> socket_;
+};
+
+} // end namespace icn_httpserver
+
+#endif // ICN_WEB_SERVER_SOCKETRESPONSE_H_
diff --git a/main.cc b/main.cc
new file mode 100644 (file)
index 0000000..13e4ee7
--- /dev/null
+++ b/main.cc
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+
+#include <iostream>
+#include <fstream>
+#include <boost/filesystem.hpp>
+
+#include "http-server/http_server.h"
+
+typedef icn_httpserver::HttpServer HttpServer;
+typedef icn_httpserver::Response Response;
+typedef icn_httpserver::Request Request;
+
+namespace std {
+
+void default_resource_send(const HttpServer &server,
+                           shared_ptr<Response> response,
+                           shared_ptr<ifstream> ifs,
+                           shared_ptr<vector<char>> buffer,
+                           std::size_t bytes_to_read) {
+  streamsize read_length;
+
+  if ((read_length = ifs->read(&(*buffer)[0], buffer->size()).gcount()) > 0) {
+    response->write(&(*buffer)[0], read_length);
+
+    if (bytes_to_read <= static_cast<streamsize>(buffer->size())) {
+      // If this is the last part of the response, send it at the pointer deletion!
+      return;
+    }
+
+    std::size_t to_read = bytes_to_read - read_length;
+    server.send(response, [&server, response, ifs, buffer, to_read](const boost::system::error_code &ec) {
+      if (!ec) {
+        default_resource_send(server, response, ifs, buffer, to_read);
+      } else {
+        cerr << "Connection interrupted" << endl;
+      }
+    });
+  }
+}
+
+void afterSignal(HttpServer *webServer, const boost::system::error_code &errorCode) {
+  cout << "\nGracefully terminating http-server... wait." << endl;
+  webServer->stop();
+}
+
+void usage(const char *programName) {
+  cerr << programName << " [-p PATH_TO_ROOT_FOOT_FOLDER] [-l WEBSERVER_PREFIX]\n"
+       << "Web server able to publish content and generate http responses over TCP/ICN\n" << endl;
+
+  exit(1);
+}
+
+int main(int argc, char **argv) {
+  // Parse command line arguments
+
+  string root_folder = "/var/www/html";
+  string webserver_prefix = "ccnx:/webserver";
+
+  int opt = 0;
+
+  while ((opt = getopt(argc, argv, "p:l:h")) != -1) {
+
+    switch (opt) {
+      case 'p':
+        root_folder = optarg;
+        break;
+      case 'l':
+        webserver_prefix = optarg;
+        break;
+      case 'h':
+        usage(argv[0]);
+        break;
+    }
+  }
+
+  if (!boost::filesystem::exists(boost::filesystem::path(root_folder))) {
+
+    // Try to create it
+    try {
+      if (!boost::filesystem::create_directories(boost::filesystem::path(root_folder))) {
+        throw boost::filesystem::filesystem_error("", boost::system::error_code());
+      }
+    } catch (boost::filesystem::filesystem_error) {
+      std::cerr << "The web root folder " << root_folder << " does not exist and its creation failed. Exiting.."
+                << std::endl;
+      return (EXIT_FAILURE);
+    }
+  }
+
+  std::cout << "Using web root folder: [" << root_folder << "]" << std::endl;
+  std::cout << "Using locator: [" << webserver_prefix << "]" << std::endl;
+
+  boost::asio::io_service io_service;
+  HttpServer server(8080, webserver_prefix, 50, 5, 300, io_service);
+
+  // GET for the path /info
+  // Responds with some server info
+  server.resource["^/info$"]["GET"] = [](shared_ptr<Response> response, shared_ptr<Request> request) {
+    stringstream content_stream;
+    content_stream << "<h1>This webserver is able to reply to HTTP over TCP/ICN</h1>";
+    content_stream << request->getMethod() << " " << request->getPath() << " HTTP/" << request->getHttp_version()
+                   << "<br>";
+
+    for (auto &header: request->getHeader()) {
+      content_stream << header.first << ": " << header.second << "<br>";
+    }
+
+    //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();
+  };
+
+  // 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, &root_folder](shared_ptr<Response> response, shared_ptr<Request> request) {
+    const auto web_root_path = boost::filesystem::canonical(root_folder);
+
+    boost::filesystem::path path = web_root_path;
+    path /= request->getPath();
+
+    if (boost::filesystem::exists(path)) {
+
+      path = boost::filesystem::canonical(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())) {
+
+        if (boost::filesystem::is_directory(path)) {
+          path /= "index.html";
+        } // default path
+
+        if (boost::filesystem::exists(path) && boost::filesystem::is_regular_file(path)) {
+
+          auto ifs = make_shared<ifstream>();
+          ifs->open(path.string(), ifstream::in | ios::binary);
+
+          if (*ifs) {
+            //read and send 1 MB at a time
+            streamsize buffer_size = 15 * 1024 * 1024;
+            auto buffer = make_shared < vector < char > > (buffer_size);
+
+            ifs->seekg(0, ios::end);
+            auto length = ifs->tellg();
+            ifs->seekg(0, ios::beg);
+
+            response->setResponseLength(length);
+
+            icn_httpserver::SocketRequest
+                *socket_request = dynamic_cast<icn_httpserver::SocketRequest *>(request.get());
+
+            if (socket_request) {
+              *response << "HTTP/1.0 200 OK\r\nContent-Length: " << length << "\r\n\r\n";
+            }
+
+            default_resource_send(server, response, ifs, buffer, length);
+
+            return;
+          }
+        }
+      }
+    }
+
+    string content = "Could not open path " + request->getPath();
+
+    *response << "HTTP/1.1 404 Not found\r\nContent-Length: " << content.length() << "\r\n\r\n" << content;
+
+  };
+
+  // Let the main thread to catch SIGINT and SIGQUIT
+  boost::asio::signal_set signals(io_service, SIGINT, SIGQUIT);
+  signals.async_wait(bind(afterSignal, &server, placeholders::_1));
+
+  thread server_thread([&server]() {
+    //Start server
+    server.start();
+  });
+
+  server_thread.join();
+
+  return 0;
+}
+
+} // end namespace std
+
+
+int main(int argc, char **argv) {
+  return std::main(argc, argv);
+}