From 46c924b9d2edd84bc6ecb5367ba52fcff82804fa Mon Sep 17 00:00:00 2001 From: Mauro Sardara Date: Tue, 18 Feb 2020 16:21:07 +0100 Subject: [HICN-528] Add progress bar to higet. Change-Id: I645ef2b8834f4310933793fb1f59e8f37e3d6aef Signed-off-by: Mauro Sardara --- apps/higet/higet.cc | 212 ++++++++++++++++----- .../src/hicn/transport/http/client_connection.cc | 82 +++----- .../src/hicn/transport/http/client_connection.h | 26 +-- libtransport/src/hicn/transport/http/message.h | 20 +- libtransport/src/hicn/transport/http/request.cc | 39 ++-- libtransport/src/hicn/transport/http/request.h | 26 ++- libtransport/src/hicn/transport/http/response.cc | 60 +++--- libtransport/src/hicn/transport/http/response.h | 17 +- libtransport/src/hicn/transport/utils/membuf.h | 9 +- 9 files changed, 276 insertions(+), 215 deletions(-) diff --git a/apps/higet/higet.cc b/apps/higet/higet.cc index fa19528f8..df34d5c14 100644 --- a/apps/higet/higet.cc +++ b/apps/higet/higet.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Cisco and/or its affiliates. + * Copyright (c) 2020 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: @@ -8,14 +8,19 @@ * * 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. + * 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 - #include +#include + +#ifndef ASIO_STANDALONE +#define ASIO_STANDALONE +#include +#endif typedef std::chrono::time_point Time; typedef std::chrono::milliseconds TimeDuration; @@ -34,61 +39,160 @@ typedef struct { std::string ipv6_first_word; } Configuration; -void processResponse(Configuration &conf, - transport::http::HTTPResponse &&response) { - auto &payload = response.getPayload(); +class ReadBytesCallbackImplementation + : public transport::http::HTTPClientConnection::ReadBytesCallback { + 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), + work_(std::make_unique(io_service_)), + thread_( + std::make_unique([this]() { io_service_.run(); })) { + std::streambuf *buf; + if (file_name_ != "-") { + of_.open(temp_file_name_, std::ofstream::binary | std::ofstream::app); + buf = of_.rdbuf(); + } else { + buf = std::cout.rdbuf(); + } - if (conf.file_name != "-") { - std::cerr << "Saving to: " << conf.file_name << " " << payload.size() - << "kB" << std::endl; + out_ = new std::ostream(buf); } - Time t3 = std::chrono::system_clock::now(); + ~ReadBytesCallbackImplementation() { + if (thread_->joinable()) { + thread_->join(); + } + } - std::streambuf *buf; - std::ofstream of; + void onBytesReceived(std::unique_ptr &&buffer) { + auto buffer_ptr = buffer.release(); + io_service_.post([this, buffer_ptr]() { + auto buffer = std::unique_ptr(buffer_ptr); + if (!first_chunk_read_) { + transport::http::HTTPResponse http_response(std::move(buffer)); + auto payload = http_response.getPayload(); + auto header = http_response.getHeaders(); + std::map::iterator it = + header.find("Content-Length"); + if (it != header.end()) { + content_size_ = yet_downloaded_ + std::stol(it->second); + } + 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(); + } + + if (file_name_ != "-") { + print_bar(byte_downloaded_, content_size_, false); + } + }); + } - if (conf.file_name != "-") { - of.open(conf.file_name, std::ofstream::binary); - buf = of.rdbuf(); - } else { - buf = std::cout.rdbuf(); + void onSuccess(std::size_t bytes) { + io_service_.post([this, bytes]() { + if (file_name_ != "-") { + of_.close(); + delete out_; + std::size_t found = file_name_.find_last_of("."); + std::string name = file_name_.substr(0, found); + std::string extension = file_name_.substr(found + 1); + if (!exists_file(file_name_)) { + std::rename(temp_file_name_.c_str(), file_name_.c_str()); + } else { + int i = 1; + std::ostringstream sstream; + sstream << name << "(" << i << ")." << extension; + std::string final_name = sstream.str(); + while (exists_file(final_name)) { + i++; + sstream.str(""); + sstream << name << "(" << i << ")." << extension; + final_name = sstream.str(); + } + std::rename(temp_file_name_.c_str(), final_name.c_str()); + } + + print_bar(100, 100, true); + std::cout << "\nDownloaded " << bytes << " bytes" << std::endl; + } + work_.reset(); + }); } - std::ostream out(buf); + void onError(const std::error_code ec) { + io_service_.post([this]() { + of_.close(); + delete out_; + work_.reset(); + }); + } + + private: + bool exists_file(const std::string &name) { + std::ifstream f(name.c_str()); + return f.good(); + } - if (conf.print_headers) { - auto &headers = response.getHeaders(); - out << "HTTP/" << response.getHttpVersion() << " " - << response.getStatusCode() << " " << response.getStatusString() - << "\n"; - for (auto &h : headers) { - out << h.first << ": " << h.second << "\n"; + void print_bar(long value, long max_value, bool last) { + float progress = (float)value / max_value; + struct winsize size; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &size); + int barWidth = size.ws_col - 8; + + std::cout << "["; + int pos = barWidth * progress; + for (int i = 0; i < barWidth; ++i) { + if (i < pos) { + std::cout << "="; + } else if (i == pos) { + std::cout << ">"; + } else { + std::cout << " "; + } + } + if (last) { + std::cout << "] " << int(progress * 100.0) << " %" << std::endl + << std::endl; + } else { + std::cout << "] " << int(progress * 100.0) << " %\r"; + std::cout.flush(); } - out << "\n"; } - out.write((char *)payload.data(), payload.size()); - of.close(); - - Time t2 = std::chrono::system_clock::now(); - TimeDuration dt = - std::chrono::duration_cast(t2 - t1); - TimeDuration dt3 = - std::chrono::duration_cast(t3 - t1); - long msec = (long)dt.count(); - long msec3 = (long)dt3.count(); - std::cerr << "Elapsed Time: " << msec / 1000.0 << " seconds -- " - << payload.size() * 8 / msec / 1000.0 << "[Mbps] -- " - << payload.size() * 8 / msec3 / 1000.0 << "[Mbps]" << std::endl; + private: + std::string file_name_; + std::string temp_file_name_; + std::ostream *out_; + std::ofstream of_; + long yet_downloaded_; + long content_size_; + bool first_chunk_read_ = false; + long byte_downloaded_ = 0; + asio::io_service io_service_; + std::unique_ptr work_; + std::unique_ptr thread_; +}; + +long checkFileStatus(std::string file_name) { + struct stat stat_buf; + std::string temp_file_name_ = file_name + ".temp"; + int rc = stat(temp_file_name_.c_str(), &stat_buf); + return rc == 0 ? stat_buf.st_size : -1; } void usage(char *program_name) { std::cerr << "usage:" << std::endl; std::cerr << program_name << " [option]... [url]..." << std::endl; std::cerr << program_name << "options:" << std::endl; - std::cerr << "-O = write documents to " - << std::endl; + std::cerr + << "-O = write documents to " + << std::endl; std::cerr << "-S = print server response" << std::endl; std::cerr << "-P = first word of the ipv6 name of " @@ -145,10 +249,23 @@ int main(int argc, char **argv) { conf.file_name = name.substr(1 + name.find_last_of("/")); } - std::map headers = {{"Host", "localhost"}, - {"User-Agent", "higet/1.0"}, - {"Connection", "Keep-Alive"}}; + long yetDownloaded = checkFileStatus(conf.file_name); + std::map headers; + if (yetDownloaded == -1) { + headers = {{"Host", "localhost"}, + {"User-Agent", "higet/1.0"}, + {"Connection", "Keep-Alive"}}; + } else { + std::string range; + range.append("bytes="); + range.append(std::to_string(yetDownloaded)); + range.append("-"); + headers = {{"Host", "localhost"}, + {"User-Agent", "higet/1.0"}, + {"Connection", "Keep-Alive"}, + {"Range", range}}; + } transport::http::HTTPClientConnection connection; if (!conf.producer_certificate.empty()) { connection.setCertificate(conf.producer_certificate); @@ -156,8 +273,11 @@ int main(int argc, char **argv) { t1 = std::chrono::system_clock::now(); - connection.get(name, headers, {}, nullptr, nullptr, conf.ipv6_first_word); - processResponse(conf, connection.response()); + http::ReadBytesCallbackImplementation readBytesCallback(conf.file_name, + yetDownloaded); + + connection.get(name, headers, {}, nullptr, &readBytesCallback, + conf.ipv6_first_word); #ifdef _WIN32 WSACleanup(); diff --git a/libtransport/src/hicn/transport/http/client_connection.cc b/libtransport/src/hicn/transport/http/client_connection.cc index aa9cb0463..bd21bc448 100644 --- a/libtransport/src/hicn/transport/http/client_connection.cc +++ b/libtransport/src/hicn/transport/http/client_connection.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2017-2020 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: @@ -16,8 +16,6 @@ #include #include -#include - #define DEFAULT_BETA 0.99 #define DEFAULT_GAMMA 0.07 @@ -40,12 +38,6 @@ HTTPClientConnection::HTTPClientConnection() std::placeholders::_2)); consumer_.setSocketOption(ConsumerCallbacksOptions::READ_CALLBACK, this); - consumer_.setSocketOption( - ConsumerCallbacksOptions::VERIFICATION_FAILED, - (ConsumerContentObjectVerificationFailedCallback)std::bind( - &HTTPClientConnection::onSignatureVerificationFailed, this, - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - consumer_.setSocketOption(GeneralTransportOptions::VERIFY_SIGNATURE, false); consumer_.connect(); std::shared_ptr portal; @@ -54,31 +46,30 @@ HTTPClientConnection::HTTPClientConnection() } HTTPClientConnection::RC HTTPClientConnection::get( - const std::string &url, HTTPHeaders headers, HTTPPayload payload, + const std::string &url, HTTPHeaders headers, HTTPPayload &&payload, std::shared_ptr response, ReadBytesCallback *callback, std::string ipv6_first_word) { - return sendRequest(url, HTTPMethod::GET, headers, payload, response, callback, - ipv6_first_word); + return sendRequest(url, HTTPMethod::GET, headers, std::move(payload), + response, callback, ipv6_first_word); } HTTPClientConnection::RC HTTPClientConnection::sendRequest( const std::string &url, HTTPMethod method, HTTPHeaders headers, - HTTPPayload payload, std::shared_ptr response, + HTTPPayload &&payload, std::shared_ptr response, ReadBytesCallback *callback, std::string ipv6_first_word) { current_url_ = url; read_bytes_callback_ = callback; if (!response) { - response = response_; + response_ = std::make_shared(); + } else { + response_ = response; } auto start = std::chrono::steady_clock::now(); - HTTPRequest request(method, url, headers, payload); - response->clear(); + request_.init(method, url, headers, std::move(payload)); success_callback_ = [this, method = std::move(method), url = std::move(url), - start = std::move(start), - response = std::move(response)]( - std::size_t size) -> std::shared_ptr { + start = std::move(start)](std::size_t size) -> void { auto end = std::chrono::steady_clock::now(); TRANSPORT_LOGI( "%s %s [%s] duration: %llu [usec] %zu [bytes]\n", @@ -87,23 +78,15 @@ HTTPClientConnection::RC HTTPClientConnection::sendRequest( std::chrono::duration_cast(end - start) .count(), size); - - return response; }; - sendRequestGetReply(request, response, ipv6_first_word); + sendRequestGetReply(ipv6_first_word); return return_code_; } -void HTTPClientConnection::verifyPacketSignature(bool verify) { - consumer_.setSocketOption(GeneralTransportOptions::VERIFY_SIGNATURE, verify); -} - -void HTTPClientConnection::sendRequestGetReply( - const HTTPRequest &request, std::shared_ptr &response, - std::string &ipv6_first_word) { - const std::string &request_string = request.getRequestString(); - const std::string &locator = request.getLocator(); +void HTTPClientConnection::sendRequestGetReply(std::string &ipv6_first_word) { + const std::string &request_string = request_.getRequestString(); + const std::string &locator = request_.getLocator(); // Hash it @@ -116,7 +99,7 @@ void HTTPClientConnection::sendRequestGetReply( ConsumerCallbacksOptions::INTEREST_OUTPUT, (ConsumerInterestCallback)std::bind( &HTTPClientConnection::processLeavingInterest, this, - std::placeholders::_1, std::placeholders::_2, request_string)); + std::placeholders::_1, std::placeholders::_2)); // Factor hicn name using hash name_.str(""); @@ -142,9 +125,9 @@ void HTTPClientConnection::sendRequestGetReply( consumer_.stop(); } -HTTPResponse HTTPClientConnection::response() { - // response_->parse(); - return std::move(*response_); +std::shared_ptr HTTPClientConnection::response() { + response_->coalescePayloadBuffer(); + return response_; } bool HTTPClientConnection::verifyData( @@ -159,10 +142,14 @@ bool HTTPClientConnection::verifyData( } void HTTPClientConnection::processLeavingInterest( - ConsumerSocket &c, const core::Interest &interest, std::string &payload) { + ConsumerSocket &c, const core::Interest &interest) { if (interest.payloadSize() == 0) { Interest &int2 = const_cast(interest); + auto payload = request_.getRequestString(); + auto payload2 = request_.getPayload(); int2.appendPayload((uint8_t *)payload.data(), payload.size()); + if (payload2) + int2.appendPayload((uint8_t *)payload2->data(), payload2->length()); } } @@ -198,21 +185,11 @@ HTTPClientConnection &HTTPClientConnection::setCertificate( return *this; } -VerificationPolicy HTTPClientConnection::onSignatureVerificationFailed( - ConsumerSocket &consumer, const core::ContentObject &content_object, - std::error_code reason) { - return VerificationPolicy::ACCEPT_PACKET; -} - // Read buffer management void HTTPClientConnection::readBufferAvailable( std::unique_ptr &&buffer) noexcept { if (!read_bytes_callback_) { - if (!read_buffer_) { - read_buffer_ = std::move(buffer); - } else { - read_buffer_->prependChain(std::move(buffer)); - } + response_->appendResponseChunk(std::move(buffer)); } else { read_bytes_callback_->onBytesReceived(std::move(buffer)); } @@ -230,18 +207,9 @@ void HTTPClientConnection::readError(const std::error_code ec) noexcept { } void HTTPClientConnection::readSuccess(std::size_t total_size) noexcept { - auto response = success_callback_(total_size); + success_callback_(total_size); if (read_bytes_callback_) { read_bytes_callback_->onSuccess(total_size); - } else { - response->reserve(total_size); - const utils::MemBuf *head = read_buffer_.get(), *current = head; - do { - response->insert(response->end(), current->data(), current->tail()); - current = current->next(); - } while (current != head); - - read_buffer_.reset(); } return_code_ = HTTPClientConnection::RC::DOWNLOAD_SUCCESS; diff --git a/libtransport/src/hicn/transport/http/client_connection.h b/libtransport/src/hicn/transport/http/client_connection.h index e001653ab..e93a37111 100644 --- a/libtransport/src/hicn/transport/http/client_connection.h +++ b/libtransport/src/hicn/transport/http/client_connection.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2017-2020 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: @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -48,18 +47,18 @@ class HTTPClientConnection : public ConsumerSocket::ReadCallback { HTTPClientConnection(); RC get(const std::string &url, HTTPHeaders headers = {}, - HTTPPayload payload = {}, + HTTPPayload &&payload = nullptr, std::shared_ptr response = nullptr, ReadBytesCallback *callback = nullptr, std::string ipv6_first_word = "b001"); RC sendRequest(const std::string &url, HTTPMethod method, - HTTPHeaders headers = {}, HTTPPayload payload = {}, + HTTPHeaders headers = {}, HTTPPayload &&payload = nullptr, std::shared_ptr response = nullptr, ReadBytesCallback *callback = nullptr, std::string ipv6_first_word = "b001"); - HTTPResponse response(); + std::shared_ptr response(); HTTPClientConnection &stop(); @@ -69,23 +68,14 @@ class HTTPClientConnection : public ConsumerSocket::ReadCallback { HTTPClientConnection &setCertificate(const std::string &cert_path); - void verifyPacketSignature(bool verify); - private: - void sendRequestGetReply(const HTTPRequest &request, - std::shared_ptr &response, - std::string &ipv6_first_word); + void sendRequestGetReply(std::string &ipv6_first_word); bool verifyData(interface::ConsumerSocket &c, const core::ContentObject &contentObject); void processLeavingInterest(interface::ConsumerSocket &c, - const core::Interest &interest, - std::string &payload); - - VerificationPolicy onSignatureVerificationFailed( - ConsumerSocket &consumer, const core::ContentObject &content_object, - std::error_code reason); + const core::Interest &interest); // Read callback bool isBufferMovable() noexcept override { return true; } @@ -106,7 +96,7 @@ class HTTPClientConnection : public ConsumerSocket::ReadCallback { // The current hICN name used for downloading std::stringstream name_; // Function to be called when the read is successful - std::function(std::size_t)> success_callback_; + std::function success_callback_; // Return code for current download RC return_code_; @@ -115,6 +105,8 @@ class HTTPClientConnection : public ConsumerSocket::ReadCallback { // any byte internally. ReadBytesCallback *read_bytes_callback_; + HTTPRequest request_; + // Internal read buffer and HTTP response, to be used if the application does // not provide any read_bytes_callback std::unique_ptr read_buffer_; diff --git a/libtransport/src/hicn/transport/http/message.h b/libtransport/src/hicn/transport/http/message.h index 270dd3f0e..b8756224f 100644 --- a/libtransport/src/hicn/transport/http/message.h +++ b/libtransport/src/hicn/transport/http/message.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2017-2020 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: @@ -19,6 +19,8 @@ #include #endif +#include + #include #include #include @@ -37,17 +39,25 @@ static std::map method_map = { }; typedef std::map HTTPHeaders; -typedef std::vector HTTPPayload; +typedef std::unique_ptr HTTPPayload; class HTTPMessage { public: virtual ~HTTPMessage() = default; - virtual const HTTPHeaders &getHeaders() = 0; + const HTTPHeaders getHeaders() { return headers_; }; + + void coalescePayloadBuffer() { + auto it = headers_.find("Content-Length"); + if (it != headers_.end()) { + auto content_length = std::stoul(it->second); + payload_->gather(content_length); + } + } - virtual const HTTPPayload &getPayload() = 0; + HTTPPayload &&getPayload() { return std::move(payload_); } - virtual const std::string &getHttpVersion() const = 0; + std::string getHttpVersion() const { return http_version_; }; protected: HTTPHeaders headers_; diff --git a/libtransport/src/hicn/transport/http/request.cc b/libtransport/src/hicn/transport/http/request.cc index 7a63b4f75..09f709642 100644 --- a/libtransport/src/hicn/transport/http/request.cc +++ b/libtransport/src/hicn/transport/http/request.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2017-2020 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: @@ -20,11 +20,15 @@ namespace transport { namespace http { -// std::map method_map +HTTPRequest::HTTPRequest() {} HTTPRequest::HTTPRequest(HTTPMethod method, const std::string &url, - const HTTPHeaders &headers, - const HTTPPayload &payload) { + const HTTPHeaders &headers, HTTPPayload &&payload) { + init(method, url, headers, std::move(payload)); +} + +void HTTPRequest::init(HTTPMethod method, const std::string &url, + const HTTPHeaders &headers, HTTPPayload &&payload) { utils::Uri uri; uri.parse(url); @@ -36,7 +40,7 @@ HTTPRequest::HTTPRequest(HTTPMethod method, const std::string &url, http_version_ = HTTP_VERSION; headers_ = headers; - payload_ = payload; + payload_ = std::move(payload); std::transform(locator_.begin(), locator_.end(), locator_.begin(), ::tolower); @@ -50,33 +54,20 @@ HTTPRequest::HTTPRequest(HTTPMethod method, const std::string &url, stream << item.first << ": " << item.second << "\r\n"; } stream << "\r\n"; - - if (payload.size() > 0) { - stream << payload.data(); - } - request_string_ = stream.str(); } -const std::string &HTTPRequest::getPort() const { return port_; } +std::string HTTPRequest::getPort() const { return port_; } -const std::string &HTTPRequest::getLocator() const { return locator_; } +std::string HTTPRequest::getLocator() const { return locator_; } -const std::string &HTTPRequest::getProtocol() const { return protocol_; } +std::string HTTPRequest::getProtocol() const { return protocol_; } -const std::string &HTTPRequest::getPath() const { return path_; } +std::string HTTPRequest::getPath() const { return path_; } -const std::string &HTTPRequest::getQueryString() const { return query_string_; } - -const HTTPHeaders &HTTPRequest::getHeaders() { return headers_; } - -const HTTPPayload &HTTPRequest::getPayload() { return payload_; } - -const std::string &HTTPRequest::getRequestString() const { - return request_string_; -} +std::string HTTPRequest::getQueryString() const { return query_string_; } -const std::string &HTTPRequest::getHttpVersion() const { return http_version_; } +std::string HTTPRequest::getRequestString() const { return request_string_; } } // namespace http diff --git a/libtransport/src/hicn/transport/http/request.h b/libtransport/src/hicn/transport/http/request.h index 1202144c0..54904d696 100644 --- a/libtransport/src/hicn/transport/http/request.h +++ b/libtransport/src/hicn/transport/http/request.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2017-2020 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: @@ -27,32 +27,28 @@ namespace http { class HTTPRequest : public HTTPMessage { public: + HTTPRequest(); HTTPRequest(HTTPMethod method, const std::string &url, - const HTTPHeaders &headers, const HTTPPayload &payload); + const HTTPHeaders &headers, HTTPPayload &&payload); - const std::string &getQueryString() const; + void init(HTTPMethod method, const std::string &url, + const HTTPHeaders &headers, HTTPPayload &&payload); - const std::string &getPath() const; + std::string getQueryString() const; - const std::string &getProtocol() const; + std::string getPath() const; - const std::string &getLocator() const; + std::string getProtocol() const; - const std::string &getPort() const; + std::string getLocator() const; - const std::string &getRequestString() const; + std::string getPort() const; - const HTTPHeaders &getHeaders() override; - - const HTTPPayload &getPayload() override; - - const std::string &getHttpVersion() const override; + std::string getRequestString() const; private: std::string query_string_, path_, protocol_, locator_, port_; std::string request_string_; - HTTPHeaders headers_; - HTTPPayload payload_; }; } // end namespace http diff --git a/libtransport/src/hicn/transport/http/response.cc b/libtransport/src/hicn/transport/http/response.cc index db7306cca..a2bc47e6b 100644 --- a/libtransport/src/hicn/transport/http/response.cc +++ b/libtransport/src/hicn/transport/http/response.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2017-2020 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: @@ -16,7 +16,8 @@ #include #include -#include +#include +#include #include @@ -26,30 +27,32 @@ namespace http { HTTPResponse::HTTPResponse() {} -HTTPResponse::HTTPResponse(const HTTPHeaders &headers, - const HTTPPayload &payload) { - headers_ = headers; - payload_ = payload; +HTTPResponse::HTTPResponse(std::unique_ptr &&response) { + parse(std::move(response)); } -const HTTPHeaders &HTTPResponse::getHeaders() { - parse(); - return headers_; -} - -const HTTPPayload &HTTPResponse::getPayload() { - parse(); - return payload_; +void HTTPResponse::appendResponseChunk( + std::unique_ptr &&response_chunk) { + if (headers_.empty()) { + parse(std::move(response_chunk)); + } else { + payload_->prependChain(std::move(response_chunk)); + } } -bool HTTPResponse::parseHeaders() { +bool HTTPResponse::parseHeaders(std::unique_ptr &&buffer) { const char *crlf2 = "\r\n\r\n"; + const char *begin = (const char *)buffer->data(); + const char *end = begin + buffer->length(); auto it = - std::search(this->begin(), this->end(), crlf2, crlf2 + strlen(crlf2)); + std::experimental::search(begin, end, + std::experimental::make_boyer_moore_searcher( + crlf2, crlf2 + strlen(crlf2))); - if (it != end()) { + if (it != end) { + buffer->trimStart(it + strlen(crlf2) - begin); std::stringstream ss; - ss.str(std::string(begin(), it + 1)); + ss.str(std::string(begin, it)); std::string line; getline(ss, line); @@ -99,24 +102,15 @@ bool HTTPResponse::parseHeaders() { } } + payload_ = std::move(buffer); + return true; } -void HTTPResponse::parse() { - if (!parseHeaders()) { +void HTTPResponse::parse(std::unique_ptr &&response) { + if (!parseHeaders(std::move(response))) { throw errors::RuntimeException("Malformed HTTP response"); } - - if (payload_.empty()) { - const char *crlf2 = "\r\n\r\n"; - auto it = - std::search(this->begin(), this->end(), crlf2, crlf2 + strlen(crlf2)); - - if (it != this->end()) { - erase(begin(), it + strlen(crlf2)); - payload_ = std::move(*dynamic_cast *>(this)); - } - } } const std::string &HTTPResponse::getStatusCode() const { return status_code_; } @@ -125,10 +119,6 @@ const std::string &HTTPResponse::getStatusString() const { return status_string_; } -const std::string &HTTPResponse::getHttpVersion() const { - return http_version_; -} - } // namespace http } // namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/response.h b/libtransport/src/hicn/transport/http/response.h index b261a128a..7ef655059 100644 --- a/libtransport/src/hicn/transport/http/response.h +++ b/libtransport/src/hicn/transport/http/response.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2017-2020 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: @@ -26,26 +26,21 @@ namespace transport { namespace http { -class HTTPResponse : public HTTPMessage, public std::vector { +class HTTPResponse : public HTTPMessage { public: - HTTPResponse(const HTTPHeaders &headers, const HTTPPayload &payload); - HTTPResponse(); - const HTTPHeaders &getHeaders() override; + HTTPResponse(std::unique_ptr &&response); - const HTTPPayload &getPayload() override; + void appendResponseChunk(std::unique_ptr &&response_chunk); const std::string &getStatusCode() const; const std::string &getStatusString() const; - const std::string &getHttpVersion() const override; - - void parse(); + void parse(std::unique_ptr &&response); - private: - bool parseHeaders(); + bool parseHeaders(std::unique_ptr &&buffer); private: std::string status_code_; diff --git a/libtransport/src/hicn/transport/utils/membuf.h b/libtransport/src/hicn/transport/utils/membuf.h index 66e9d7afa..9fc37dd25 100644 --- a/libtransport/src/hicn/transport/utils/membuf.h +++ b/libtransport/src/hicn/transport/utils/membuf.h @@ -526,12 +526,11 @@ class MemBuf { // * // * Returns ByteRange that points to the data MemBuf stores. // */ - // ByteRange coalesceWithHeadroomTailroom( - // std::size_t newHeadroom, - // std::size_t newTailroom) { + // ByteRange coalesceWithHeadroomTailroom(std::size_t newHeadroom, + // std::size_t newTailroom) { // if (isChained()) { - // coalesceAndReallocate( - // newHeadroom, computeChainDataLength(), this, newTailroom); + // coalesceAndReallocate(newHeadroom, computeChainDataLength(), this, + // newTailroom); // } // return ByteRange(data_, length_); // } -- cgit 1.2.3-korg