diff --git a/include/opendht/http.h b/include/opendht/http.h index 1626e0c32377764240fe81ef1c6117785ba94feb..2b70366cd7e7050e3b354cb59c662d5402b03c42 100644 --- a/include/opendht/http.h +++ b/include/opendht/http.h @@ -221,6 +221,9 @@ public: inline const Url& get_url() const { return resolver_->get_url(); }; + inline std::string& to_string() { + return request_; + } void set_certificate(std::shared_ptr<dht::crypto::Certificate> certificate); void set_logger(std::shared_ptr<dht::Logger> logger); diff --git a/src/http.cpp b/src/http.cpp index 5970fe030dc26de855b740513c8a1665f968154e..051143339a605953ce245426926b8a837cd2655b 100644 --- a/src/http.cpp +++ b/src/http.cpp @@ -43,8 +43,10 @@ Url::Url(const std::string& url): url(url) const size_t proto_end = url.find("://"); if (proto_end != std::string::npos){ addr_begin = proto_end + 3; - if (url.substr(0, proto_end) == "https") + if (url.substr(0, proto_end) == "https"){ protocol = "https"; + service = protocol; + } } // host and service size_t addr_size = url.substr(addr_begin).find("/"); @@ -173,7 +175,8 @@ Connection::set_endpoint(const asio::ip::tcp::endpoint& endpoint, const asio::ss auto verifier = asio::ssl::rfc2818_verification(hostname); bool verified = verifier(preverified, ctx); auto verify_ec = X509_STORE_CTX_get_error(ctx.native_handle()); - if (verify_ec == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN /*19*/) + if (verify_ec == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT /*18*/ + || verify_ec == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN /*19*/) verified = true; return verified; } @@ -231,10 +234,13 @@ Connection::async_handshake(HandlerCb cb) if (ec == asio::error::operation_aborted) return; auto verify_ec = SSL_get_verify_result(ssl_socket_->asio_ssl_stream().native_handle()); - if (verify_ec == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN /*19*/ and logger_) - logger_->d("[http:client] [connection:%i] allow self-signed certificate in handshake", id_); - else if (verify_ec != X509_V_OK and logger_) - logger_->e("[http:client] [connection:%i] verify handshake error: %i", id_, verify_ec); + if (logger_){ + if (verify_ec == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT /*18*/ + || verify_ec == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN /*19*/) + logger_->d("[http:client] [connection:%i] allow self-signed certificate in handshake", id_); + else if (verify_ec != X509_V_OK) + logger_->e("[http:client] [connection:%i] verify handshake error: %i", id_, verify_ec); + } if (cb) cb(ec); }); @@ -520,14 +526,18 @@ void Request::build() { std::stringstream request; + bool append_body = true; // first header request << header_.method().c_str() << " " << header_.request_target() << " " << "HTTP/" << header_.http_major() << "." << header_.http_minor() << "\r\n"; // other headers - for (auto header: headers_) + for (auto header: headers_){ request << restinio::field_to_string(header.first) << ": " << header.second << "\r\n"; + if (header.first == restinio::http_field_t::expect and header.second == "100-continue") + append_body = false; + } // last connection header std::string conn_str = ""; @@ -546,13 +556,11 @@ Request::build() request << "Connection: " << conn_str << "\r\n"; // body & content-length - if (!body_.empty()){ + if (!body_.empty()) request << "Content-Length: " << body_.size() << "\r\n\r\n"; - request << body_; - } - // last delim - request << "\r\n"; + if (append_body) + request << body_ << "\r\n"; request_ = request.str(); } @@ -713,6 +721,7 @@ Request::connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, HandlerCb cb) if (certificate_) conn_->set_endpoint(endpoint, asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert); + if (conn_ and conn_->is_open() and conn_->is_ssl()){ conn_->async_handshake([this, cb](const asio::error_code& ec){ if (ec == asio::error::operation_aborted) @@ -851,9 +860,17 @@ Request::handle_response_header(const asio::error_code& ec) headers.append(header + "\n"); } headers.append("\n"); - // parse the headers parse_request(headers); + if (headers_[restinio::http_field_t::expect] == "100-continue" and response_.status_code != 200){ + notify_state_change(State::SENDING); + request_.append(body_); + std::ostream request_stream(&conn_->input()); + request_stream << body_ << "\r\n"; + conn_->async_write(std::bind(&Request::handle_request, this, std::placeholders::_1)); + return; + } + // has content-length auto content_length_it = response_.headers.find(HTTP_HEADER_CONTENT_LENGTH); if (content_length_it != response_.headers.end()) diff --git a/tests/httptester.cpp b/tests/httptester.cpp index 7021cdc8dfb0cf50861d930713cfa9e55063ad23..0a6a861573625432bf89006cf6999adf7a13a34b 100644 --- a/tests/httptester.cpp +++ b/tests/httptester.cpp @@ -53,6 +53,20 @@ HttpTester::test_parse_url() { CPPUNIT_ASSERT(parsed.target == "/"); } +void +HttpTester::test_parse_https_url_no_service() { + // Arrange + std::string url = "https://jami.net/"; + // Act + dht::http::Url parsed (url); + // Assert + CPPUNIT_ASSERT(parsed.url == url); + CPPUNIT_ASSERT(parsed.protocol == "https"); + CPPUNIT_ASSERT(parsed.host == "jami.net"); + CPPUNIT_ASSERT(parsed.service == "https"); + CPPUNIT_ASSERT(parsed.target == "/"); +} + void HttpTester::test_parse_url_no_prefix_no_target() { // Arrange diff --git a/tests/httptester.h b/tests/httptester.h index 14cab3872177ce50ff77a889a02dc46476c90870..83df193f544c85bb45f716aacad4a2ee98dec183 100644 --- a/tests/httptester.h +++ b/tests/httptester.h @@ -31,6 +31,7 @@ namespace test { class HttpTester : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(HttpTester); CPPUNIT_TEST(test_parse_url); + CPPUNIT_TEST(test_parse_https_url_no_service); CPPUNIT_TEST(test_parse_url_no_prefix_no_target); CPPUNIT_TEST(test_parse_url_target); CPPUNIT_TEST(test_parse_url_query); @@ -57,6 +58,7 @@ class HttpTester : public CppUnit::TestFixture { * Test parse urls */ void test_parse_url(); + void test_parse_https_url_no_service(); void test_parse_url_no_prefix_no_target(); void test_parse_url_target(); void test_parse_url_query();