Skip to content

Commit

Permalink
improve performance by reducing response fragments
Browse files Browse the repository at this point in the history
  • Loading branch information
303248153 committed Jan 21, 2020
1 parent 5c92874 commit b0da79c
Show file tree
Hide file tree
Showing 9 changed files with 260 additions and 21 deletions.
38 changes: 33 additions & 5 deletions include/CPVFramework/Http/HttpConstantStrings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ namespace cpv::constants {
static const constexpr char GatewayTimeout[] = "Gateway Timeout";
static const constexpr char HttpVersionNotSupported[] = "HTTP Version Not Supported";

// standard request fields
// standard request header fields
static const constexpr char AIM[] = "A-IM";
static const constexpr char Accept[] = "Accept";
static const constexpr char AcceptCharset[] = "Accept-Charset";
Expand Down Expand Up @@ -130,13 +130,13 @@ namespace cpv::constants {
static const constexpr char TE[] = "TE";
static const constexpr char UserAgent[] = "User-Agent";

// non-standard request fields
// non-standard request header fields
static const constexpr char UpgradeInsecureRequests[] = "Upgrade-Insecure-Requests";
static const constexpr char XRequestedWith[] = "X-Requested-With";
static const constexpr char DNT[] = "DNT";
static const constexpr char XCsrfToken[] = "X-Csrf-Token";

// standard response fields
// standard response header fields
static const constexpr char AccessControlAllowOrigin[] = "Access-Control-Allow-Origin";
static const constexpr char AccessControlAllowCredentials[] = "Access-Control-Allow-Credentials";
static const constexpr char AccessControlExposeHeaders[] = "Access-Control-Expose-Headers";
Expand Down Expand Up @@ -184,14 +184,42 @@ namespace cpv::constants {
static const constexpr char WWWAuthenticate[] = "WWW-Authenticate";
static const constexpr char XFrameOptions[] = "X-Frame-Options";

// non-standard response fields
// non-standard response header fields
static const constexpr char Refresh[] = "Refresh";

// standard response values
// standard response header values
static const constexpr char Chunked[] = "chunked";
static const constexpr char Keepalive[] = "keep-alive";
static const constexpr char Close[] = "close";
static const constexpr char TextPlainUtf8[] = "text/plain;charset=utf-8";
static const constexpr char ApplicationJsonUtf8[] = "application/json;charset=utf-8";

// reduce fragments for common headers
namespace with_crlf_colonspace {
// request header fields
static const constexpr char Host[] = "\r\nHost: ";
static const constexpr char ContentType[] = "\r\nContent-Type: ";
static const constexpr char ContentLength[] = "\r\nContent-Length: ";
static const constexpr char Connection[] = "\r\nConnection: ";
static const constexpr char Pragma[] = "\r\nPragma: ";
static const constexpr char UpgradeInsecureRequests[] = "\r\nUpgrade-Insecure-Requests: ";
static const constexpr char DNT[] = "\r\nDNT: ";
static const constexpr char UserAgent[] = "\r\nUser-Agent: ";
static const constexpr char Accept[] = "\r\nAccept: ";
static const constexpr char AcceptEncoding[] = "\r\nAccept-Encoding: ";
static const constexpr char AcceptLanguage[] = "\r\nAccept-Language: ";
static const constexpr char Cookie[] = "\r\nCookie: ";
static const constexpr char XRequestedWith[] = "\r\nX-Requested-With: ";
// response header fields
static const constexpr char Date[] = "\r\nDate: ";
static const constexpr char ContentEncoding[] = "\r\nContent-Encoding: ";
static const constexpr char TransferEncoding[] = "\r\nTransfer-Encoding: ";
static const constexpr char Server[] = "\r\nServer: ";
static const constexpr char Vary[] = "\r\nVary: ";
static const constexpr char ETag[] = "\r\nETag: ";
static const constexpr char CacheControl[] = "\r\nCache-Control: ";
static const constexpr char Expires[] = "\r\nExpires: ";
static const constexpr char LastModified[] = "\r\nLast-Modified: ";
}
}

7 changes: 7 additions & 0 deletions include/CPVFramework/Http/HttpRequestHeaders.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once
#include "../Allocators/StackAllocator.hpp"
#include "../Utility/SharedString.hpp"
#include "../Utility/Packet.hpp"
#include "./HttpConstantStrings.hpp"

namespace cpv {
Expand Down Expand Up @@ -56,6 +57,12 @@ namespace cpv {
}
}

/**
* Append all headers to packet fragments for http 1.
* notice it will append crlf to begin but not to end.
*/
void appendToHttp1Packet(Packet::MultipleFragments& fragments);

/** Set header value */
void setHeader(SharedString&& key, SharedString&& value);

Expand Down
7 changes: 7 additions & 0 deletions include/CPVFramework/Http/HttpResponseHeaders.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <utility>
#include "../Allocators/StackAllocator.hpp"
#include "../Utility/SharedString.hpp"
#include "../Utility/Packet.hpp"
#include "./HttpConstantStrings.hpp"

namespace cpv {
Expand Down Expand Up @@ -60,6 +61,12 @@ namespace cpv {
}
}

/**
* Append all headers to packet fragments for http 1.
* notice it will append crlf to begin but not to end.
*/
void appendToHttp1Packet(Packet::MultipleFragments& fragments);

/** Set header value */
void setHeader(SharedString&& key, SharedString&& value);

Expand Down
1 change: 1 addition & 0 deletions include/CPVFramework/Utility/ConstantStrings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace cpv::constants {
static const constexpr char ColonSlashSlash[] = "://";
static const constexpr char LF[] = "\n";
static const constexpr char CRLF[] = "\r\n";
static const constexpr char CRLFCRLF[] = "\r\n\r\n";
static const constexpr char True[] = "true";
static const constexpr char False[] = "false";
static const constexpr char Null[] = "null";
Expand Down
63 changes: 63 additions & 0 deletions src/Http/HttpRequestHeaders.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,69 @@ namespace cpv {
}
}

/** Append all headers to packet fragments for http 1 */
void HttpRequestHeaders::appendToHttp1Packet(Packet::MultipleFragments& fragments) {
namespace cs = constants::with_crlf_colonspace;
if (!host_.empty()) {
fragments.append(cs::Host);
fragments.append(host_.share());
}
if (!contentType_.empty()) {
fragments.append(cs::ContentType);
fragments.append(contentType_.share());
}
if (!contentLength_.empty()) {
fragments.append(cs::ContentLength);
fragments.append(contentLength_.share());
}
if (!connection_.empty()) {
fragments.append(cs::Connection);
fragments.append(connection_.share());
}
if (!pragma_.empty()) {
fragments.append(cs::Pragma);
fragments.append(pragma_.share());
}
if (!upgradeInsecureRequests_.empty()) {
fragments.append(cs::UpgradeInsecureRequests);
fragments.append(upgradeInsecureRequests_.share());
}
if (!dnt_.empty()) {
fragments.append(cs::DNT);
fragments.append(dnt_.share());
}
if (!userAgent_.empty()) {
fragments.append(cs::UserAgent);
fragments.append(userAgent_.share());
}
if (!accept_.empty()) {
fragments.append(cs::Accept);
fragments.append(accept_.share());
}
if (!acceptEncoding_.empty()) {
fragments.append(cs::AcceptEncoding);
fragments.append(acceptEncoding_.share());
}
if (!acceptLanguage_.empty()) {
fragments.append(cs::AcceptLanguage);
fragments.append(acceptLanguage_.share());
}
if (!cookie_.empty()) {
fragments.append(cs::Cookie);
fragments.append(cookie_.share());
}
if (!xRequestedWith_.empty()) {
fragments.append(cs::XRequestedWith);
fragments.append(xRequestedWith_.share());
}
for (auto& pair : remainHeaders_) {
fragments.append(constants::CRLF);
fragments.append(pair.first.share());
fragments.append(constants::ColonSpace);
fragments.append(pair.second.share());
}
}

/** Get header value */
SharedString HttpRequestHeaders::getHeader(const SharedString& key) const {
auto it = Internal::FixedMembers.find(key);
Expand Down
65 changes: 65 additions & 0 deletions src/Http/HttpResponseHeaders.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,71 @@ namespace cpv {
{ constants::LastModified, &HttpResponseHeaders::lastModified_ },
});

/** Append all headers to packet fragments for http 1 */
void HttpResponseHeaders::appendToHttp1Packet(Packet::MultipleFragments& fragments) {
namespace cs = constants::with_crlf_colonspace;
if (!date_.empty()) {
fragments.append(cs::Date);
fragments.append(date_.share());
}
if (!contentType_.empty()) {
fragments.append(cs::ContentType);
fragments.append(contentType_.share());
}
if (!contentLength_.empty()) {
fragments.append(cs::ContentLength);
fragments.append(contentLength_.share());
}
if (!contentEncoding_.empty()) {
fragments.append(cs::ContentEncoding);
fragments.append(contentEncoding_.share());
}
if (!transferEncoding_.empty()) {
fragments.append(cs::TransferEncoding);
fragments.append(transferEncoding_.share());
}
if (!connection_.empty()) {
fragments.append(cs::Connection);
fragments.append(connection_.share());
}
if (!server_.empty()) {
fragments.append(cs::Server);
fragments.append(server_.share());
}
if (!vary_.empty()) {
fragments.append(cs::Vary);
fragments.append(vary_.share());
}
if (!etag_.empty()) {
fragments.append(cs::ETag);
fragments.append(etag_.share());
}
if (!cacheControl_.empty()) {
fragments.append(cs::CacheControl);
fragments.append(cacheControl_.share());
}
if (!expires_.empty()) {
fragments.append(cs::Expires);
fragments.append(expires_.share());
}
if (!lastModified_.empty()) {
fragments.append(cs::LastModified);
fragments.append(lastModified_.share());
}
for (auto& pair : remainHeaders_) {
fragments.append(constants::CRLF);
fragments.append(pair.first.share());
fragments.append(constants::ColonSpace);
fragments.append(pair.second.share());
}
for (auto& pair : additionHeaders_) {
fragments.append(constants::CRLF);
fragments.append(pair.first.share());
fragments.append(constants::ColonSpace);
fragments.append(pair.second.share());
}
}

/** Set header value */
void HttpResponseHeaders::setHeader(SharedString&& key, SharedString&& value) {
auto it = Internal::FixedMembers.find(key);
Expand Down
22 changes: 6 additions & 16 deletions src/HttpServer/Connections/Http11ServerConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -424,14 +424,12 @@ namespace cpv {
/** (for reply loop) Get maximum fragments count for response headers */
std::size_t Http11ServerConnection::getResponseHeadersFragmentsCount() const {
// calculate fragments count
// +6: version, space, status code, space, status message, crlf
// +4: date header, colon + space, header value, crlf
// +4: server header, colon + space, header value, crlf
// +4: connection header, colon + space, header value, crlf
// +5: version, space, status code, space, status message
// + headers count * 4
// +1: crlf
// +1: crlfcrlf
// +1: reserved
auto& response = processingContext_.getResponse();
return 19 + response.getHeaders().maxSize() * 4;
return 7 + response.getHeaders().maxSize() * 4;
}

/** (for reply loop) Append response headers to packet, please check responseHeadersAppended first */
Expand Down Expand Up @@ -481,23 +479,15 @@ namespace cpv {
}
}
// append response headers to packet
// manipulate fragments vector directly to avoid variant and boundary checks
auto& fragments = packet.getOrConvertToMultiple();
fragments.reserveAddition(getResponseHeadersFragmentsCount());
fragments.append(response.getVersion().share());
fragments.append(constants::Space);
fragments.append(response.getStatusCode().share());
fragments.append(constants::Space);
fragments.append(response.getStatusMessage().share());
fragments.append(constants::CRLF);
responseHeaders.foreach([&fragments]
(const SharedString& key, const SharedString& value) {
fragments.append(key.share());
fragments.append(constants::ColonSpace);
fragments.append(value.share());
fragments.append(constants::CRLF);
});
fragments.append(constants::CRLF);
responseHeaders.appendToHttp1Packet(fragments);
fragments.append(constants::CRLFCRLF);
}

/** (for reply loop) Determine whether keep connection or not by checking connection header */
Expand Down
40 changes: 40 additions & 0 deletions tests/Cases/Http/TestHttpRequest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,46 @@ TEST(HttpRequest, headersForeach) {
"AdditionC: TestAdditionC\r\n");
}

TEST(HttpRequest, headersAppendToHttp1Packet) {
cpv::HttpRequest request;
auto& headers = request.getHeaders();
headers.setHost("TestHost");
headers.setContentType("TestContentType");
headers.setContentLength("TestContentLength");
headers.setConnection("TestConnection");
headers.setPragma("TestPragma");
headers.setUpgradeInsecureRequests("TestUpgradeInsecureRequests");
headers.setDNT("TestDNT");
headers.setUserAgent("TestUserAgent");
headers.setAccept("TestAccept");
headers.setAcceptEncoding("TestAcceptEncoding");
headers.setAcceptLanguage("TestAcceptLanguage");
headers.setCookie("TestCookie");
headers.setXRequestedWith("TestXRequestedWith");
headers.setHeader("AdditionA", "TestAdditionA");
headers.setHeader("AdditionB", "TestAdditionB");
headers.setHeader("AdditionC", "TestAdditionC");
cpv::Packet packet;
headers.appendToHttp1Packet(packet.getOrConvertToMultiple());
ASSERT_EQ(packet.toString(),
"\r\nHost: TestHost\r\n"
"Content-Type: TestContentType\r\n"
"Content-Length: TestContentLength\r\n"
"Connection: TestConnection\r\n"
"Pragma: TestPragma\r\n"
"Upgrade-Insecure-Requests: TestUpgradeInsecureRequests\r\n"
"DNT: TestDNT\r\n"
"User-Agent: TestUserAgent\r\n"
"Accept: TestAccept\r\n"
"Accept-Encoding: TestAcceptEncoding\r\n"
"Accept-Language: TestAcceptLanguage\r\n"
"Cookie: TestCookie\r\n"
"X-Requested-With: TestXRequestedWith\r\n"
"AdditionA: TestAdditionA\r\n"
"AdditionB: TestAdditionB\r\n"
"AdditionC: TestAdditionC");
}

TEST(HttpRequest, headersNotConstructible) {
ASSERT_FALSE(std::is_constructible_v<cpv::HttpRequestHeaders>);
ASSERT_FALSE(std::is_copy_constructible_v<cpv::HttpRequestHeaders>);
Expand Down
38 changes: 38 additions & 0 deletions tests/Cases/Http/TestHttpResponse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,44 @@ TEST(HttpResponse, headersForeach) {
"AdditionC: TestAdditionC\r\n");
}

TEST(HttpResponse, headersAppendToHttp1Packet) {
cpv::HttpResponse response;
auto& headers = response.getHeaders();
headers.setDate("TestDate");
headers.setContentType("TestContentType");
headers.setContentLength("TestContentLength");
headers.setContentEncoding("TestContentEncoding");
headers.setTransferEncoding("TestTransferEncoding");
headers.setConnection("TestConnection");
headers.setServer("TestServer");
headers.setVary("TestVary");
headers.setETag("TestETag");
headers.setCacheControl("TestCacheControl");
headers.setExpires("TestExpires");
headers.setLastModified("TestLastModified");
headers.setHeader("AdditionA", "TestAdditionA");
headers.setHeader("AdditionB", "TestAdditionB");
headers.setHeader("AdditionC", "TestAdditionC");
cpv::Packet packet;
headers.appendToHttp1Packet(packet.getOrConvertToMultiple());
ASSERT_EQ(packet.toString(),
"\r\nDate: TestDate\r\n"
"Content-Type: TestContentType\r\n"
"Content-Length: TestContentLength\r\n"
"Content-Encoding: TestContentEncoding\r\n"
"Transfer-Encoding: TestTransferEncoding\r\n"
"Connection: TestConnection\r\n"
"Server: TestServer\r\n"
"Vary: TestVary\r\n"
"ETag: TestETag\r\n"
"Cache-Control: TestCacheControl\r\n"
"Expires: TestExpires\r\n"
"Last-Modified: TestLastModified\r\n"
"AdditionA: TestAdditionA\r\n"
"AdditionB: TestAdditionB\r\n"
"AdditionC: TestAdditionC");
}

TEST(HttpResponse, additionHeaders) {
cpv::HttpResponse response;
auto& headers = response.getHeaders();
Expand Down

0 comments on commit b0da79c

Please sign in to comment.