From 063f343083b64760214d625d05889a04ee1cd8c3 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 4 Jan 2019 00:22:29 +0000 Subject: WIP --- icespider/embedded/clientSocket.cpp | 65 +++++++++++++++++++++++++++++++----- icespider/embedded/clientSocket.h | 17 +++++++--- icespider/embedded/socketHandler.h | 6 ++-- icespider/unittests/testEmbedded.cpp | 52 +++++++++++++++++++++++++++-- 4 files changed, 124 insertions(+), 16 deletions(-) diff --git a/icespider/embedded/clientSocket.cpp b/icespider/embedded/clientSocket.cpp index 54aa7cb..900f5d0 100644 --- a/icespider/embedded/clientSocket.cpp +++ b/icespider/embedded/clientSocket.cpp @@ -5,13 +5,21 @@ #include #include #include +#include namespace IceSpider::Embedded { static socklen_t clientlen = sizeof(struct sockaddr_in); + static const std::string_view crlf("\r\n"); + static const std::string_view clnsp(": "); ClientSocket::ClientSocket(int pfd) : - SocketHandler(accept4(pfd, (struct sockaddr *) &clientaddr, &clientlen, SOCK_NONBLOCK)), - buf(BUFSIZ), + ClientSocket(accept4(pfd, (struct sockaddr *) &clientaddr, &clientlen, SOCK_NONBLOCK), false) + { + } + + ClientSocket::ClientSocket(int fd, bool) : + SocketHandler(fd), + buf(BUFSIZ, '\0'), rec(0), state(State::reading_headers) { @@ -40,13 +48,17 @@ namespace IceSpider::Embedded { }; int r; while ((r = readBytes()) > 0) { - buf[rec + r] = 0; - auto end = strstr(&buf.at(rec > 3 ? rec - 2 : 0), "\r\n"); - if (end) { - auto w = ::write(fd, "HTTP/1.1 204 No content\r\n\r\n", 27); - (void)w; + // Recommended ending + if (auto end = buf.find("\r\n\r\n", rec > 4 ? rec - 4 : 0, 4); end != std::string::npos) { + process_request(std::string_view(buf).substr(0, end)); + rec = 0; + buf.erase(0, end + 3); + } + // Alternative ending + else if (auto end = buf.find("\r\r", rec > 1 ? rec - 2 : 0, 2); end != std::string::npos) { + process_request(std::string_view(buf).substr(0, end)); rec = 0; - buf.erase(buf.begin(), buf.begin() + (end - &buf.front())); + buf.erase(0, end + 1); } else { rec += r; @@ -58,6 +70,43 @@ namespace IceSpider::Embedded { return -1; } + void ClientSocket::process_request(const std::string_view & hdrs) + { + parse_request(hdrs); + auto w = ::write(fd, "HTTP/1.1 204 No content\r\n\r\n", 27); + (void)w; + (void)hdrs; + } + + void ClientSocket::parse_request(std::string_view hdrs) + { + auto consumeUpto = [&hdrs](auto endAt, auto skip) { + if (auto end = hdrs.find_first_of(endAt); end != std::string_view::npos) { + auto rtn = hdrs.substr(0, end); + if (auto skipTo = hdrs.find_first_not_of(skip, end); skipTo != std::string_view::npos) { + end = skipTo; + } + hdrs.remove_prefix(end); + return rtn; + } + else { + auto rtn = hdrs; + hdrs.remove_prefix(hdrs.length()); + return rtn; + } + }; + method = Slicer::ModelPartForEnum<::IceSpider::HttpMethod>::lookup( + //TODO: string copy + std::string(consumeUpto(' ', ' '))); + url = consumeUpto(' ', ' '); + version = consumeUpto(crlf, crlf); + while (!hdrs.empty()) { + auto n = consumeUpto(':', clnsp); + auto v = consumeUpto(crlf, crlf); + headers.insert_or_assign(n, v); + } + } + int ClientSocket::stream_input() { return 0; diff --git a/icespider/embedded/clientSocket.h b/icespider/embedded/clientSocket.h index 72449a0..b475eef 100644 --- a/icespider/embedded/clientSocket.h +++ b/icespider/embedded/clientSocket.h @@ -2,20 +2,24 @@ #define ICESPIDER_EMBEDDED_CLIENTSOCKET_H #include "socketHandler.h" -#include +#include #include +#include namespace IceSpider::Embedded { - class ClientSocket : public SocketHandler { + class DLL_PUBLIC ClientSocket : public SocketHandler { public: ClientSocket(int fd); int read(Listener * listener) override; int write(Listener * listener) override; - private: + protected: + ClientSocket(int fd, bool unused); inline int read_headers(); inline int stream_input(); + void process_request(const std::string_view & hdrs); + void parse_request(std::string_view hdrs); enum class State { reading_headers, @@ -23,9 +27,14 @@ namespace IceSpider::Embedded { }; struct sockaddr_in clientaddr; - std::vector buf; + std::string buf; std::size_t rec; State state; + + ::IceSpider::HttpMethod method; + std::string_view url, version; + typedef std::map Headers; + Headers headers; }; } diff --git a/icespider/embedded/socketHandler.h b/icespider/embedded/socketHandler.h index 1c3e2d8..e796275 100644 --- a/icespider/embedded/socketHandler.h +++ b/icespider/embedded/socketHandler.h @@ -1,13 +1,15 @@ #ifndef ICESPIDER_EMBEDDED_SOCKETHANDLER_H #define ICESPIDER_EMBEDDED_SOCKETHANDLER_H +#include + namespace IceSpider::Embedded { class Listener; - class SocketHandler { + class DLL_PUBLIC SocketHandler { public: SocketHandler(int f); - ~SocketHandler(); + virtual ~SocketHandler(); virtual int read(Listener *) = 0; virtual int write(Listener *) = 0; diff --git a/icespider/unittests/testEmbedded.cpp b/icespider/unittests/testEmbedded.cpp index e77b114..72f60f3 100644 --- a/icespider/unittests/testEmbedded.cpp +++ b/icespider/unittests/testEmbedded.cpp @@ -1,10 +1,14 @@ #define BOOST_TEST_MODULE TestEmbedded -#include +#include #include #include +#include +#include #include +BOOST_TEST_DONT_PRINT_LOG_VALUE(IceSpider::HttpMethod); + class EmbeddedIceSpiderInstance : public IceSpider::Embedded::Listener { public: EmbeddedIceSpiderInstance() : @@ -51,6 +55,7 @@ BOOST_AUTO_TEST_CASE(startup_and_shutdown_cycle, * boost::unit_test::timeout(2)) BOOST_FIXTURE_TEST_SUITE(EmbeddedIceSpider, EmbeddedIceSpiderRunner); BOOST_AUTO_TEST_CASE(simple_curl_test, + * boost::unit_test::disabled() // Not usually interested * boost::unit_test::timeout(5)) { BOOST_REQUIRE_EQUAL(0, system("curl -s http://localhost:18080/")); @@ -58,6 +63,7 @@ BOOST_AUTO_TEST_CASE(simple_curl_test, // Throw some requests at it, get a general performance overview BOOST_AUTO_TEST_CASE(quick_siege_test, + * boost::unit_test::disabled() // Not usually interested * boost::unit_test::timeout(5)) { if (wrk_exists()) { @@ -67,7 +73,7 @@ BOOST_AUTO_TEST_CASE(quick_siege_test, // Throw lots of requests at it, get a good performance overview BOOST_AUTO_TEST_CASE(simple_performance_test, - * boost::unit_test::disabled() // Not usually interested + //* boost::unit_test::disabled() // Not usually interested * boost::unit_test::timeout(15)) { if (wrk_exists()) { @@ -77,3 +83,45 @@ BOOST_AUTO_TEST_CASE(simple_performance_test, BOOST_AUTO_TEST_SUITE_END(); +struct TestClientSocket : public IceSpider::Embedded::ClientSocket { + TestClientSocket() : IceSpider::Embedded::ClientSocket(0, false) { } +}; + +BOOST_FIXTURE_TEST_SUITE(Client, TestClientSocket); + +BOOST_AUTO_TEST_CASE(get_root_1_1, * boost::unit_test::timeout(1)) +{ + parse_request("GET / HTTP/1.1"); + BOOST_CHECK_EQUAL(method, IceSpider::HttpMethod::GET); + BOOST_CHECK_EQUAL(url, "/"); + BOOST_CHECK_EQUAL(version, "HTTP/1.1"); + BOOST_CHECK(headers.empty()); +} + +BOOST_AUTO_TEST_CASE(post_path_1_0, * boost::unit_test::timeout(1)) +{ + parse_request("POST /path/to/resource.cpp HTTP/1.0"); + BOOST_CHECK_EQUAL(method, IceSpider::HttpMethod::POST); + BOOST_CHECK_EQUAL(url, "/path/to/resource.cpp"); + BOOST_CHECK_EQUAL(version, "HTTP/1.0"); + BOOST_CHECK(headers.empty()); +} + +BOOST_AUTO_TEST_CASE(get_root_1_1_hdr_alt, * boost::unit_test::timeout(1)) +{ + parse_request("GET / HTTP/1.1\nAccept: text/html"); + BOOST_CHECK_EQUAL(headers.size(), 1); + BOOST_CHECK_EQUAL(headers.begin()->first, "Accept"); + BOOST_CHECK_EQUAL(headers["Accept"], "text/html"); +} + +BOOST_AUTO_TEST_CASE(get_root_1_1_hdrs, * boost::unit_test::timeout(1)) +{ + parse_request("GET / HTTP/1.1\r\nAccept: text/html\r\nEtag: 43"); + BOOST_CHECK_EQUAL(headers.size(), 2); + BOOST_CHECK_EQUAL(headers["Accept"], "text/html"); + BOOST_CHECK_EQUAL(headers["Etag"], "43"); +} + +BOOST_AUTO_TEST_SUITE_END(); + -- cgit v1.2.3