diff options
-rw-r--r-- | apps/higet/higet.cc | 74 | ||||
-rw-r--r-- | apps/http-proxy/src/ATSConnector.cc | 92 | ||||
-rw-r--r-- | apps/http-proxy/src/ATSConnector.h | 13 | ||||
-rw-r--r-- | apps/http-proxy/src/HTTP1.xMessageFastParser.cc | 19 | ||||
-rw-r--r-- | apps/http-proxy/src/HTTP1.xMessageFastParser.h | 8 | ||||
-rw-r--r-- | docs/README.md | 2 | ||||
-rw-r--r-- | docs/source/apps.md | 133 | ||||
-rw-r--r-- | docs/source/index.rst | 10 | ||||
-rw-r--r-- | docs/source/interface.md | 42 | ||||
-rw-r--r-- | docs/source/started.md | 25 | ||||
-rw-r--r-- | docs/source/telemetry.md | 10 | ||||
-rw-r--r-- | libtransport/src/hicn/transport/http/response.cc | 46 | ||||
-rw-r--r-- | libtransport/src/hicn/transport/http/response.h | 6 |
13 files changed, 333 insertions, 147 deletions
diff --git a/apps/higet/higet.cc b/apps/higet/higet.cc index df34d5c14..fcb0cc540 100644 --- a/apps/higet/higet.cc +++ b/apps/higet/higet.cc @@ -17,6 +17,9 @@ #include <fstream> #include <map> +#include <experimental/algorithm> +#include <experimental/functional> + #ifndef ASIO_STANDALONE #define ASIO_STANDALONE #include <asio.hpp> @@ -41,12 +44,16 @@ typedef struct { class ReadBytesCallbackImplementation : public transport::http::HTTPClientConnection::ReadBytesCallback { + static std::string chunk_separator; + public: ReadBytesCallbackImplementation(std::string file_name, long yet_downloaded) : file_name_(file_name), temp_file_name_(file_name_ + ".temp"), yet_downloaded_(yet_downloaded), byte_downloaded_(yet_downloaded), + chunked_(false), + chunk_size_(0), work_(std::make_unique<asio::io_service::work>(io_service_)), thread_( std::make_unique<std::thread>([this]() { io_service_.run(); })) { @@ -71,21 +78,72 @@ class ReadBytesCallbackImplementation auto buffer_ptr = buffer.release(); io_service_.post([this, buffer_ptr]() { auto buffer = std::unique_ptr<utils::MemBuf>(buffer_ptr); + std::unique_ptr<utils::MemBuf> payload; if (!first_chunk_read_) { transport::http::HTTPResponse http_response(std::move(buffer)); - auto payload = http_response.getPayload(); + payload = http_response.getPayload(); auto header = http_response.getHeaders(); + content_size_ = yet_downloaded_; std::map<std::string, std::string>::iterator it = header.find("Content-Length"); if (it != header.end()) { - content_size_ = yet_downloaded_ + std::stol(it->second); + content_size_ += std::stol(it->second); + } else { + it = header.find("Transfer-Encoding"); + if (it != header.end() && it->second.compare("chunked") == 0) { + chunked_ = true; + } } - out_->write((char *)payload->data(), payload->length()); first_chunk_read_ = true; - byte_downloaded_ += payload->length(); } else { - out_->write((char *)buffer->data(), buffer->length()); - byte_downloaded_ += buffer->length(); + payload = std::move(buffer); + } + + if (chunked_) { + if (chunk_size_ > 0) { + out_->write((char *)payload->data(), chunk_size_); + payload->trimStart(chunk_size_); + + if (payload->length() >= chunk_separator.size()) { + payload->trimStart(chunk_separator.size()); + } + } + + while (payload->length() > 0) { + // read next chunk size + const char *begin = (const char *)payload->data(); + const char *end = (const char *)payload->tail(); + + using std::experimental::make_boyer_moore_searcher; + auto it = std::experimental::search( + begin, end, + make_boyer_moore_searcher(chunk_separator.begin(), + chunk_separator.end())); + if (it != end) { + chunk_size_ = std::stoul(begin, 0, 16); + content_size_ += chunk_size_; + payload->trimStart(it + chunk_separator.size() - begin); + + std::size_t to_write; + if (payload->length() >= chunk_size_) { + to_write = chunk_size_; + } else { + to_write = payload->length(); + chunk_size_ -= payload->length(); + } + + out_->write((char *)payload->data(), to_write); + byte_downloaded_ += to_write; + payload->trimStart(to_write); + + if (payload->length() >= chunk_separator.size()) { + payload->trimStart(chunk_separator.size()); + } + } + } + } else { + out_->write((char *)payload->data(), payload->length()); + byte_downloaded_ += payload->length(); } if (file_name_ != "-") { @@ -174,11 +232,15 @@ class ReadBytesCallbackImplementation long content_size_; bool first_chunk_read_ = false; long byte_downloaded_ = 0; + bool chunked_; + std::size_t chunk_size_; asio::io_service io_service_; std::unique_ptr<asio::io_service::work> work_; std::unique_ptr<std::thread> thread_; }; +std::string ReadBytesCallbackImplementation::chunk_separator = "\r\n"; + long checkFileStatus(std::string file_name) { struct stat stat_buf; std::string temp_file_name_ = file_name + ".temp"; diff --git a/apps/http-proxy/src/ATSConnector.cc b/apps/http-proxy/src/ATSConnector.cc index f656a68cb..a9b889941 100644 --- a/apps/http-proxy/src/ATSConnector.cc +++ b/apps/http-proxy/src/ATSConnector.cc @@ -33,6 +33,9 @@ ATSConnector::ATSConnector(asio::io_service &io_service, timer_(io_service), is_reconnection_(false), data_available_(false), + content_length_(0), + is_last_chunk_(false), + chunked_(false), receive_callback_(receive_callback), on_reconnect_callback_(on_reconnect_callback) { input_buffer_.prepare(buffer_size + 2048); @@ -96,23 +99,26 @@ void ATSConnector::doWrite() { }); } // namespace transport -void ATSConnector::handleRead(std::error_code ec, std::size_t length, - std::size_t size) { +void ATSConnector::handleRead(std::error_code ec, std::size_t length) { if (TRANSPORT_EXPECT_TRUE(!ec)) { - size -= length; + content_length_ -= length; const uint8_t *buffer = asio::buffer_cast<const uint8_t *>(input_buffer_.data()); - receive_callback_(buffer, input_buffer_.size(), !size, false); + receive_callback_(buffer, input_buffer_.size(), !content_length_, false); input_buffer_.consume(input_buffer_.size()); - if (!size) { - doReadHeader(); + if (!content_length_) { + if (!chunked_ || is_last_chunk_) { + doReadHeader(); + } else { + doReadChunkedHeader(); + } } else { - auto to_read = size >= buffer_size ? buffer_size : size; - asio::async_read( - socket_, input_buffer_, asio::transfer_exactly(to_read), - std::bind(&ATSConnector::handleRead, this, std::placeholders::_1, - std::placeholders::_2, size)); + auto to_read = + content_length_ >= buffer_size ? buffer_size : content_length_; + asio::async_read(socket_, input_buffer_, asio::transfer_exactly(to_read), + std::bind(&ATSConnector::handleRead, this, + std::placeholders::_1, std::placeholders::_2)); } } else if (ec == asio::error::eof) { input_buffer_.consume(input_buffer_.size()); @@ -129,20 +135,47 @@ void ATSConnector::doReadBody(std::size_t body_size, ? (buffer_size - input_buffer_.size()) : bytes_to_read; + is_last_chunk_ = chunked_ && body_size == 5; + if (to_read > 0) { - asio::async_read( - socket_, input_buffer_, asio::transfer_exactly(to_read), - std::bind(&ATSConnector::handleRead, this, std::placeholders::_1, - std::placeholders::_2, bytes_to_read)); + content_length_ = bytes_to_read; + asio::async_read(socket_, input_buffer_, asio::transfer_exactly(to_read), + std::bind(&ATSConnector::handleRead, this, + std::placeholders::_1, std::placeholders::_2)); } else { const uint8_t *buffer = asio::buffer_cast<const uint8_t *>(input_buffer_.data()); - receive_callback_(buffer, body_size, !to_read, false); + receive_callback_(buffer, body_size, chunked_ ? is_last_chunk_ : !to_read, + false); input_buffer_.consume(body_size); - doReadHeader(); + + if (!chunked_ || is_last_chunk_) { + doReadHeader(); + } else { + doReadChunkedHeader(); + } } } +void ATSConnector::doReadChunkedHeader() { + asio::async_read_until( + socket_, input_buffer_, "\r\n", + [this](std::error_code ec, std::size_t length) { + if (TRANSPORT_EXPECT_TRUE(!ec)) { + const uint8_t *buffer = + asio::buffer_cast<const uint8_t *>(input_buffer_.data()); + std::size_t chunk_size = + std::stoul(reinterpret_cast<const char *>(buffer), 0, 16) + 2 + + length; + auto additional_bytes = input_buffer_.size(); + doReadBody(chunk_size, additional_bytes); + } else { + input_buffer_.consume(input_buffer_.size()); + tryReconnection(); + } + }); +} + void ATSConnector::doReadHeader() { asio::async_read_until( socket_, input_buffer_, "\r\n\r\n", @@ -150,14 +183,31 @@ void ATSConnector::doReadHeader() { if (TRANSPORT_EXPECT_TRUE(!ec)) { const uint8_t *buffer = asio::buffer_cast<const uint8_t *>(input_buffer_.data()); - std::size_t size = HTTPMessageFastParser::hasBody(buffer, length); + auto headers = HTTPMessageFastParser::getHeaders(buffer, length); - auto additional_bytes = input_buffer_.size() - length; + // Try to get content length, if available + auto it = headers.find(HTTPMessageFastParser::content_length); + std::size_t size = 0; + if (it != headers.end()) { + size = std::stoull(it->second); + chunked_ = false; + } else { + it = headers.find(HTTPMessageFastParser::transfer_encoding); + if (it != headers.end() && + it->second.compare(HTTPMessageFastParser::chunked) == 0) { + chunked_ = true; + } + } - receive_callback_(buffer, length, !size, true); + receive_callback_(buffer, length, !size && !chunked_, true); + auto additional_bytes = input_buffer_.size() - length; input_buffer_.consume(length); - doReadBody(size, additional_bytes); + if (!chunked_) { + doReadBody(size, additional_bytes); + } else { + doReadChunkedHeader(); + } } else { input_buffer_.consume(input_buffer_.size()); tryReconnection(); diff --git a/apps/http-proxy/src/ATSConnector.h b/apps/http-proxy/src/ATSConnector.h index dbec30353..8d91b7b7b 100644 --- a/apps/http-proxy/src/ATSConnector.h +++ b/apps/http-proxy/src/ATSConnector.h @@ -65,12 +65,17 @@ class ATSConnector { void doReadBody(std::size_t body_size, std::size_t additional_bytes); + // void handleReadChunked(std::error_code ec, std::size_t length, + // std::size_t size); + + void doReadChunkedHeader(); + void doWrite(); bool checkConnected(); private: - void handleRead(std::error_code ec, std::size_t length, std::size_t bytes); + void handleRead(std::error_code ec, std::size_t length); void tryReconnection(); void startConnectionTimer(); void handleDeadline(const std::error_code &ec); @@ -88,6 +93,12 @@ class ATSConnector { bool is_reconnection_; bool data_available_; + std::size_t content_length_; + + // Chunked encoding + bool is_last_chunk_; + bool chunked_; + ContentReceivedCallback receive_callback_; OnReconnect on_reconnect_callback_; diff --git a/apps/http-proxy/src/HTTP1.xMessageFastParser.cc b/apps/http-proxy/src/HTTP1.xMessageFastParser.cc index a03871649..729eb3aeb 100644 --- a/apps/http-proxy/src/HTTP1.xMessageFastParser.cc +++ b/apps/http-proxy/src/HTTP1.xMessageFastParser.cc @@ -15,17 +15,36 @@ #include "HTTP1.xMessageFastParser.h" +#include <hicn/transport/http/response.h> + #include <experimental/algorithm> #include <experimental/functional> #include <iostream> std::string HTTPMessageFastParser::numbers = "0123456789"; std::string HTTPMessageFastParser::content_length = "Content-Length"; +std::string HTTPMessageFastParser::transfer_encoding = "Transfer-Encoding"; +std::string HTTPMessageFastParser::chunked = "chunked"; std::string HTTPMessageFastParser::cache_control = "Cache-Control"; std::string HTTPMessageFastParser::mpd = "mpd"; std::string HTTPMessageFastParser::connection = "Connection"; std::string HTTPMessageFastParser::separator = "\r\n\r\n"; +HTTPHeaders HTTPMessageFastParser::getHeaders(const uint8_t *headers, + std::size_t length) { + HTTPHeaders ret; + std::string http_version; + std::string status_code; + std::string status_string; + + if (transport::http::HTTPResponse::parseHeaders(headers, length, ret, http_version, + status_code, status_string)) { + return ret; + } + + throw std::runtime_error("Error parsing response headers."); +} + std::size_t HTTPMessageFastParser::hasBody(const uint8_t *headers, std::size_t length) { const char *buffer = reinterpret_cast<const char *>(headers); diff --git a/apps/http-proxy/src/HTTP1.xMessageFastParser.h b/apps/http-proxy/src/HTTP1.xMessageFastParser.h index 10a70c3e9..79dbce19d 100644 --- a/apps/http-proxy/src/HTTP1.xMessageFastParser.h +++ b/apps/http-proxy/src/HTTP1.xMessageFastParser.h @@ -18,15 +18,21 @@ #include <algorithm> #include <string> +#include <hicn/transport/http/message.h> + +using transport::http::HTTPHeaders; + class HTTPMessageFastParser { public: + static HTTPHeaders getHeaders(const uint8_t* headers, std::size_t length); static std::size_t hasBody(const uint8_t* headers, std::size_t length); static bool isMpdRequest(const uint8_t* headers, std::size_t length); static uint32_t parseCacheControl(const uint8_t* headers, std::size_t length); - private: static std::string numbers; static std::string content_length; + static std::string transfer_encoding; + static std::string chunked; static std::string cache_control; static std::string connection; static std::string mpd; diff --git a/docs/README.md b/docs/README.md index d7a6c6cc8..be7f4e6df 100644 --- a/docs/README.md +++ b/docs/README.md @@ -26,7 +26,7 @@ $ source env/bin/activate $ pip install -r docs/etc/requirements.txt $ cd docs -Which installs all the required applications into it's own, isolated, +Which installs all the required applications into its own, isolated, virtual environment, so as to not interfere with other builds that may use different versions of software. diff --git a/docs/source/apps.md b/docs/source/apps.md index 1c44a8ca9..0a1d6eb20 100644 --- a/docs/source/apps.md +++ b/docs/source/apps.md @@ -3,7 +3,7 @@ The open source distribution provides a few application examples: one MPEG-DASH video player, and an HTTP reverse proxy a command line HTTP GET client. -hICN sockets have been succesfully used to serve HTTP, RTP and +hICN sockets have been successfully used to serve HTTP, RTP and RSockets application protocols. ## Dependencies @@ -94,7 +94,7 @@ For running the hicn-plugin at the server there are two main alternatives: - Use a docker container - Run the hicn-plugin directly in a VM or Bare Metal Server -### Docker +### Docker VPP hICN proxy Install docker in the server VM: @@ -102,86 +102,77 @@ Install docker in the server VM: server$ curl get.docker.com | bash ``` -Run the hicn-http-proxy container. Here we use a public server "example.com" as origin: +Run the hicn-http-proxy container. Here we use a public server at "localhost" as origin and +HTTP traffic is server with an IPv6 name prefix b001. ```bash -server$ docker run -e ORIGIN_ADDRESS=example.com \ - -e ORIGIN_PORT=80 \ - -e CACHE_SIZE=10000 \ - -e HICN_MTU=1200 \ - -e FIRST_IPV6_WORD=c001 \ - -e HICN_PREFIX=http://webserver \ - --privileged \ - --name vhttpproxy \ - -d icnteam/vhttpproxy +#!/bin/bash + +#Http proxy options +ORIGIN_ADDRESS=${ORIGIN_ADDRESS:-"localhost"} +ORIGIN_PORT=${ORIGIN_PORT:-"80"} +CACHE_SIZE=${CACHE_SIZE:-"10000"} +DEFAULT_CONTENT_LIFETIME=${DEFAULT_CONTENT_LIFETIME:-"7200"} +HICN_MTU=${HICN_MTU:-"1300"} +FIRST_IPV6_WORD=${FIRST_IPV6_WORD:-"b001"} +USE_MANIFEST=${USE_MANIFEST:-"true"} +HICN_PREFIX=${HICN_PREFIX:-"http://webserver"} + +# UDP Punting +HICN_LISTENER_PORT=${HICN_LISTENER_PORT:-33567} +TAP_ADDRESS_VPP=192.168.0.2 +TAP_ADDRESS_KER=192.168.0.1 +TAP_ADDRESS_NET=192.168.0.0/24 +TAP_ID=0 +TAP_NAME=tap${TAP_ID} + +vppctl create tap id ${TAP_ID} +vppctl set int state ${TAP_NAME} up +vppctl set interface ip address tap0 ${TAP_ADDRESS_VPP}/24 +ip addr add ${TAP_ADDRESS_KER}/24 brd + dev ${TAP_NAME} + +# Redirect the udp traffic on port 33567 (The one used for hicn) to VPP +iptables -t nat -A PREROUTING -p udp --dport ${HICN_LISTENER_PORT} -j DNAT \ + --to-destination ${TAP_ADDRESS_VPP}:${HICN_LISTENER_PORT} +# Masquerade all the traffic coming from VPP +iptables -t nat -A POSTROUTING -j MASQUERADE --src ${TAP_ADDRESS_NET} ! \ + --dst ${TAP_ADDRESS_NET} -o eth0 +# Add default route to vpp +vppctl ip route add 0.0.0.0/0 via ${TAP_ADDRESS_KER} ${TAP_NAME} +# Set UDP punting +vppctl hicn punting add prefix ${FIRST_IPV6_WORD}::/16 intfc ${TAP_NAME}\ + type udp4 dst_port ${HICN_LISTENER_PORT} + +# Run the http proxy +PARAMS="-a ${ORIGIN_ADDRESS} " +PARAMS+="-p ${ORIGIN_PORT} " +PARAMS+="-c ${CACHE_SIZE} " +PARAMS+="-m ${HICN_MTU} " +PARAMS+="-P ${FIRST_IPV6_WORD} " +PARAMS+="-l ${DEFAULT_CONTENT_LIFETIME} " +if [ "${USE_MANIFEST}" = "true" ]; then + PARAMS+="-M " +fi + +hicn-http-proxy ${PARAMS} ${HICN_PREFIX} ``` -Create a hicn private network: +Docker images of the example above are available at +<https://hub.docker.com/r/icnteam/vhttpproxy>. +Images can be pulled using the following tags. -```bash -server$ GATEWAY=192.168.0.254 -server$ docker network create --subnet 192.168.0.0/24 --gateway ${GATEWAY} hicn-network -``` - -Connect the proxy container to the hicn network: - -```bash -server$ docker network connect hicn-network vhttpproxy +```shell +docker pull icnteam/vhttpproxy:amd64 +docker pull icnteam/vhttpproxy:arm64 ``` -Connect the hicn network to the vpp forwarder: - -```bash -server$ IP_ADDRESS=$(docker inspect -f "{{with index .NetworkSettings.Networks \"hicn-network\"}}{{.IPAddress}}{{end}}" vhttpproxy) -server$ INTERFACE=$(docker exec -it vhttpproxy ifconfig | grep -B 1 ${IP_ADDRESS} | awk 'NR==1 {gsub(":","",$1); print $1}') -server$ docker exec -it vhttpproxy ip addr flush dev ${INTERFACE} -server$ docker exec -it vhttpproxy ethtool -K ${INTERFACE} tx off rx off ufo off gso off gro off tso off -server$ docker exec -it vhttpproxy vppctl create host-interface name ${INTERFACE} -server$ docker exec -it vhttpproxy vppctl set interface state host-${INTERFACE} up -server$ docker exec -it vhttpproxy vppctl set interface ip address host-${INTERFACE} ${IP_ADDRESS}/24 -server$ docker exec -it vhttpproxy vppctl ip route add 10.0.0.0/24 via ${GATEWAY} host-eth1 -``` - -Set the punting: - -```bash -server$ PORT=12345 -server$ docker exec -it vhttpproxy vppctl hicn punting add prefix c001::/16 intfc host-${INTERFACE} type udp4 src_port ${PORT} dst_port ${PORT} -``` - -Docker containers are cool, but sometimes they do not allow you to do simple operations like expose ports while the container is already running. But we have a workaround for this :) - -```bash -server$ sudo iptables -t nat -A DOCKER -p udp --dport ${PORT} -j DNAT --to-destination ${IP_ADDRESS}:${PORT} -server$ sudo iptables -t nat -A POSTROUTING -j MASQUERADE -p udp --source ${IP_ADDRESS} --destination ${IP_ADDRESS} --dport ${PORT} -server$ sudo iptables -A DOCKER -j ACCEPT -p udp --destination ${IP_ADDRESS} --dport ${PORT} -``` - -In the client, install the hicn stack: - -```bash -client$ sudo apt-get install -y hicn-light hicn-apps -``` - -Create a configuration file for the hicn-light forwarder. Here we are configuring UDP faces: - -```bash -client$ mkdir -p ${HOME}/etc -client$ LOCAL_IP="10.0.0.2" # Put here the actual IPv4 of the local interface -client$ LOCAL_PORT="12345" -client$ REMOTE_IP="10.0.0.1" # Put here the actual IPv4 of the remote interface -client$ REMOTE_PORT="12345" -client$ cat << EOF > ${HOME}/etc/hicn-light.conf -add listener udp list0 ${LOCAL_IP} ${LOCAL_PORT} -add connection udp conn0 ${REMOTE_IP} ${REMOTE_PORT} ${LOCAL_IP} ${LOCAL_PORT} -add route conn0 c001::/16 1 -EOF -``` +#### Client side Run the hicn-light forwarder ```bash -client$ sudo /usr/bin/hicn-light-daemon --daemon --capacity 1000 --log-file ${HOME}/hicn-light.log --config ${HOME}/etc/hicn-light.conf +client$ sudo /usr/bin/hicn-light-daemon --daemon --capacity 1000 --log-file \ + ${HOME}/hicn-light.log --config ${HOME}/etc/hicn-light.conf ``` Run the http client [higet](#higet) and print the http response on stdout: diff --git a/docs/source/index.rst b/docs/source/index.rst index 17aeb3a1c..99ea39afa 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,6 +1,16 @@ Hybrid Information-Centric Networking ===================================== +Hybrid Information-Centric Networking (hICN) is a network architecture that makes +use of IPv6 or IPv4 to realize location-independent communications. It is largely +inspired by the pioneer work of Van Jacobson on Content-Centric Networking, that was +a clean-slate architecture whereas hICN is based on the Internet protocol and easy to +deploy in today networks and applications. hICN brings many-to-many communications, +multi-homing, multi-path, multi-source, group communications to the Internet protocol +without replicated unicast. The project implements novel transport protocols, with a socket API, +for real-time and capacity seeking applications. A scalable stack is available based +on VPP and a client stack is provided to support any mobile and desktop operating system. + .. toctree:: started diff --git a/docs/source/interface.md b/docs/source/interface.md index 13f1007a3..9e6b6f383 100644 --- a/docs/source/interface.md +++ b/docs/source/interface.md @@ -38,7 +38,7 @@ interfaces: protocol to discover a remote hICN forwarder that might be needed to establish overlay faces. -- network\_framework [MacOS, iOS] +- network_framework [MacOS, iOS] This component uses the recommended Network framework on Apple devices, which provided all required information to query faces in a unified API: @@ -82,11 +82,11 @@ The face manager source code includes a template that can be used as a skeleton to develop new faces. It can be found in `src/interface/dummy/dummy.{h,c}`. Both include guard and specific interface functions are prefixed by a (short) identifier which acts as a namespace for interface specific code (in our case -the string 'dummy\_'). +the string 'dummy_'). -Registration and instanciation of the different interfaces is currently done at +Registration and instantiation of the different interfaces is currently done at compile time in the file `src/api.c`, and the appropriate hooks to use the dummy -interface are avaialble in the code between `#if 0/#endif` tags. +interface are available in the code between `#if 0/#endif` tags. #### Interface template header; configuration parameters @@ -96,7 +96,7 @@ the interface, if any. In the template, these configuration options are empty: -```C# +```C /* * Configuration data */ @@ -124,7 +124,7 @@ interface. Each interface can hold a pointer to an internal data structure, which is declared as follows: -```C# +```C /* * Internal data */ @@ -142,7 +142,7 @@ typedef struct { ``` We find here a copy of the configuration settings (which allows the called to -instanciate the structure on the stack), as well as a file descriptor +instantiate the structure on the stack), as well as a file descriptor (assuming most interfaces will react on events on a file descriptor). The rest of the file consists in the implementation of the interface, in @@ -150,8 +150,8 @@ particular the different function required by the registration of a new interface to the system. They are grouped as part of the `interface_ops_t` data structure declared at the end of the file: -```C# -interface\_ops\_t dummy\_ops = { +```C +interface_ops_t dummy_ops = { .type = "dummy", .initialize = dummy_initialize, .finalize = dummy_finalize, @@ -170,14 +170,14 @@ typedef struct { /** The type given to the interfaces */ char * type; /* Constructor */ - int (*initialize)(struct interface\_s * interface, void * cfg); + int (*initialize)(struct interface_s * interface, void * cfg); /* Destructor */ int (*finalize)(struct interface_s * interface); /* Callback upon file descriptor event (iif previously registered) */ int (*callback)(struct interface_s * interface); /* Callback upon facelet events coming from the face manager */ int (*on_event)(struct interface_s * interface, const struct facelet_s * facelet); -} interface\_ops\_t; +} interface_ops_t; ``` Such an interface has to be registered first, then one (or multiple) instance(s) @@ -196,9 +196,9 @@ if (rc < 0) goto ERR_REGISTER; ``` -- interface instanciation: +- interface instantiation: -```C++ +```C #include "interfaces/dummy/dummy.h" /* [...] */ @@ -219,7 +219,7 @@ In the template, the constructor is the most involved as it need to: - initialize the internal data structure: -```C# +```C dummy_data_t * data = malloc(sizeof(dummy_data_t)); if (!data) goto ERR_MALLOC; @@ -228,7 +228,7 @@ In the template, the constructor is the most involved as it need to: - process configuration parameters, eventually setting some default values: -```C# +```C /* Use default values for unspecified configuration parameters */ if (cfg) { data->cfg = *(dummy_cfg_t *)cfg; @@ -246,7 +246,7 @@ event loop for read events. A return value of 0 means the interface does not require any file descriptor. As usual, a negative return value indicates an error. -```C# +```C data->fd = 0; /* ... */ @@ -273,14 +273,14 @@ In order to retrieve the internal data structure, that should in particular store such a file descriptor, all other function but the constructor can dereference it from the interface pointer they receive as parameter: -```C++ -dummy\_data\_t * data = (dummy\_data\_t*)interface->data; +```C +dummy_data_t * data = (dummy_data_t*)interface->data; ``` #### Raising and Receiving Events An interface will receive events in the form of a facelet through the `*_on_event` -function. It can then use the facelet API we have describe above to read +function. It can then use the facelet API we have described above to read information about the face. As this information is declared const, the interface can either create a new @@ -290,7 +290,7 @@ clone it. The facelet event can then be defined and raised to the face maanger for further processing through the following code: -```C++ +```C facelet_set_event(facelet, EVENT_TYPE_CREATE); interface_raise_event(interface, facelet); ``` @@ -361,5 +361,5 @@ An example illustrating how to connect to the dummy service from `updownsrv` is provided as the `updown` interface in the facemgr source code. This interface periodically swaps the status of the LTE interface up and down. -It is instanciated as part of the facemgr codebase when the code is compiled +It is instantiated as part of the facemgr codebase when the code is compiled with the ``-DWITH_EXAMPLE_UPDOWN` cmake option. diff --git a/docs/source/started.md b/docs/source/started.md index 168ce1272..3c5181297 100644 --- a/docs/source/started.md +++ b/docs/source/started.md @@ -90,13 +90,30 @@ Coming soon. ### Docker -Several docker images are nightly built with the latest software for Ubuntu 18 LTS (amd64/arm64), and available on docker hub -at <https://hub.docker.com/u/icnteam>. +Several docker images are nightly built with the latest software for Ubuntu 18 LTS (amd64/arm64), and available on docker hub at <https://hub.docker.com/u/icnteam>. + +The following images are nightly built and maintained. + +```shell +docker pull icnteam/vswitch:amd64 +docker pull icnteam/vswitch:arm64 + +docker pull icnteam/vserver:amd64 +docker pull icnteam/vserver:arm64 + +docker pull icnteam/vhttpproxy:amd64 +docker pull icnteam/vhttpproxy:arm64 +``` ### Vagrant -Vagrant boxes for a virtual switch are available at <https://app.vagrantup.com/icnteam/boxes/vswitch>. -Supported providers are kvm, vmware and virtualbox. +Vagrant boxes for a virtual switch are available at +<https://app.vagrantup.com/icnteam> + +```shell +vagrant box add icnteam/vswitch +``` +Supported providers are libvirt, vmware and virtualbox. ## License diff --git a/docs/source/telemetry.md b/docs/source/telemetry.md index 0af3b0e02..868958f2b 100644 --- a/docs/source/telemetry.md +++ b/docs/source/telemetry.md @@ -27,9 +27,9 @@ $ make $ sudo make install ``` -## Using hICN collectd plugins ## +## Using hICN collectd plugins -### Platforms ### +### Platforms hICN collectd plugins have been tested in: @@ -40,7 +40,7 @@ hICN collectd plugins have been tested in: - CentOS 7 -### Dependencies ### +### Dependencies Build dependencies: @@ -51,7 +51,7 @@ Build dependencies: - vpp-dev - hicn-plugin-dev -## Getting started ## +## Getting started Collectd needs to be configured in order to use the hICN collectd plugins. The configuration can be achieved editing the file '/etc/collectd/collectd.conf' and adding the following lines: @@ -67,7 +67,7 @@ Before running collectd, a vpp forwarder must be started. If the vpp-hicn plugin Edit the configuration file as the following: -``` +```html ###################################################################### # Global # ###################################################################### diff --git a/libtransport/src/hicn/transport/http/response.cc b/libtransport/src/hicn/transport/http/response.cc index a2bc47e6b..ba0acd1ac 100644 --- a/libtransport/src/hicn/transport/http/response.cc +++ b/libtransport/src/hicn/transport/http/response.cc @@ -41,16 +41,33 @@ void HTTPResponse::appendResponseChunk( } bool HTTPResponse::parseHeaders(std::unique_ptr<utils::MemBuf> &&buffer) { + auto ret = + HTTPResponse::parseHeaders(buffer->data(), buffer->length(), headers_, + http_version_, status_code_, status_string_); + + if (ret) { + buffer->trimStart(ret); + payload_ = std::move(buffer); + return true; + } + + return false; +} + +std::size_t HTTPResponse::parseHeaders(const uint8_t *buffer, std::size_t size, + HTTPHeaders &headers, + std::string &http_version, + std::string &status_code, + std::string &status_string) { const char *crlf2 = "\r\n\r\n"; - const char *begin = (const char *)buffer->data(); - const char *end = begin + buffer->length(); + const char *begin = (const char *)buffer; + const char *end = begin + size; auto it = std::experimental::search(begin, end, std::experimental::make_boyer_moore_searcher( crlf2, crlf2 + strlen(crlf2))); if (it != end) { - buffer->trimStart(it + strlen(crlf2) - begin); std::stringstream ss; ss.str(std::string(begin, it)); @@ -58,29 +75,28 @@ bool HTTPResponse::parseHeaders(std::unique_ptr<utils::MemBuf> &&buffer) { getline(ss, line); std::istringstream line_s(line); std::string _http_version; - std::string http_version; line_s >> _http_version; std::size_t separator; if ((separator = _http_version.find('/')) != std::string::npos) { if (_http_version.substr(0, separator) != "HTTP") { - return false; + return 0; } - http_version_ = + http_version = line.substr(separator + 1, _http_version.length() - separator - 1); } else { - return false; + return 0; } - std::string status_code, status_string; + std::string _status_string; - line_s >> status_code_; - line_s >> status_string; + line_s >> status_code; + line_s >> _status_string; auto _it = std::search(line.begin(), line.end(), status_string.begin(), status_string.end()); - status_string_ = std::string(_it, line.end() - 1); + status_string = std::string(_it, line.end() - 1); std::size_t param_end; std::size_t value_start; @@ -92,19 +108,17 @@ bool HTTPResponse::parseHeaders(std::unique_ptr<utils::MemBuf> &&buffer) { value_start++; } if (value_start < line.size()) { - headers_[line.substr(0, param_end)] = + headers[line.substr(0, param_end)] = line.substr(value_start, line.size() - value_start - 1); } } } else { - return false; + return 0; } } } - payload_ = std::move(buffer); - - return true; + return it + strlen(crlf2) - begin; } void HTTPResponse::parse(std::unique_ptr<utils::MemBuf> &&response) { diff --git a/libtransport/src/hicn/transport/http/response.h b/libtransport/src/hicn/transport/http/response.h index 7ef655059..bab41acb8 100644 --- a/libtransport/src/hicn/transport/http/response.h +++ b/libtransport/src/hicn/transport/http/response.h @@ -42,6 +42,12 @@ class HTTPResponse : public HTTPMessage { bool parseHeaders(std::unique_ptr<utils::MemBuf> &&buffer); + static std::size_t parseHeaders(const uint8_t *buffer, std::size_t size, + HTTPHeaders &headers, + std::string &http_version, + std::string &status_code, + std::string &status_string); + private: std::string status_code_; std::string status_string_; |