Compare commits

...

18 Commits

Author SHA1 Message Date
Martin Chang
9d80aaa1e9 update README 2023-11-08 11:18:15 +08:00
Martin Chang
75f197ecd4 remove forced printing in debug 2023-11-08 11:04:20 +08:00
Martin Chang
8ce2b853d9 fix MSVC build 2023-11-08 10:59:08 +08:00
Martin Chang
781ef3c194 track window size 2023-11-08 10:43:19 +08:00
Martin Chang
3eddcafe73 send request body 2023-11-08 10:32:19 +08:00
Martin Chang
09a8634838 handle CR LF in header 2023-11-08 10:23:17 +08:00
Martin Chang
3ea63787e7 respect max concurrent streams 2023-11-08 10:20:20 +08:00
Martin Chang
7202330f10 reply to pings 2023-11-08 10:15:44 +08:00
Martin Chang
e27b800b33 handle out-of-order core frames 2023-11-08 10:06:36 +08:00
Martin Chang
98678dd331 handle response with no body 2023-11-08 09:49:54 +08:00
Martin Chang
f2a7ac8b2f handle error 2023-11-08 00:41:36 +08:00
Martin Chang
89786de0fe slight cleanup for error handling 2023-11-07 17:16:45 +08:00
Martin Chang
49cfea3b4a if content-length is present, cehck it matches the amount of data in DATA frame 2023-11-07 16:11:54 +08:00
Martin Chang
a3a6267577 fix 2023-11-07 15:50:11 +08:00
Martin Chang
69f592b726 release streamId on fail to decode header 2023-11-07 15:49:44 +08:00
Martin Chang
a0a9f7a337 enable handling of multiple streams 2023-11-07 15:48:54 +08:00
Martin Chang
1a27e8bf1e some minor improvments 2023-11-07 14:03:21 +08:00
Martin Chang
05a18675fe optimize frame serialization 2023-11-07 13:33:29 +08:00
5 changed files with 815 additions and 401 deletions

View File

@ -13,7 +13,7 @@ Drogon is a cross-platform framework, It supports Linux, macOS, FreeBSD, OpenBSD
* Use a non-blocking I/O network lib based on epoll (kqueue under macOS/FreeBSD) to provide high-concurrency, high-performance network IO, please visit the [TFB Tests Results](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=composite) for more details; * Use a non-blocking I/O network lib based on epoll (kqueue under macOS/FreeBSD) to provide high-concurrency, high-performance network IO, please visit the [TFB Tests Results](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=composite) for more details;
* Provide a completely asynchronous programming mode; * Provide a completely asynchronous programming mode;
* Support Http1.0/1.1 (server side and client side); * Support HTTP/2 (and 1.0/1.1) client and HTTP 1.1/1.0 server
* Based on template, a simple reflection mechanism is implemented to completely decouple the main program framework, controllers and views. * Based on template, a simple reflection mechanism is implemented to completely decouple the main program framework, controllers and views.
* Support cookies and built-in sessions; * Support cookies and built-in sessions;
* Support back-end rendering, the controller generates the data to the view to generate the Html page. Views are described by CSP template files, C++ codes are embedded into Html pages through CSP tags. And the drogon command-line tool automatically generates the C++ code files for compilation; * Support back-end rendering, the controller generates the data to the view to generate the Html page. Views are described by CSP template files, C++ codes are embedded into Html pages through CSP tags. And the drogon command-line tool automatically generates the C++ code files for compilation;

View File

@ -16,7 +16,7 @@ int main()
{ {
trantor::Logger::setLogLevel(trantor::Logger::kTrace); trantor::Logger::setLogLevel(trantor::Logger::kTrace);
{ {
auto client = HttpClient::newHttpClient("https://clehaxze.tw:8844", auto client = HttpClient::newHttpClient("https://clehaxze.tw",
nullptr, nullptr,
false, false,
false); false);
@ -48,7 +48,7 @@ int main()
req->setParameter("wd", "wx"); req->setParameter("wd", "wx");
req->setParameter("oq", "wx"); req->setParameter("oq", "wx");
for (int i = 0; i < 1; ++i) for (int i = 0; i < 2; ++i)
{ {
client->sendRequest( client->sendRequest(
req, [](ReqResult result, const HttpResponsePtr &response) { req, [](ReqResult result, const HttpResponsePtr &response) {
@ -76,7 +76,6 @@ int main()
}); });
LOG_INFO << "send request"; LOG_INFO << "send request";
} }
app().run();
} }
app().run();
} }

File diff suppressed because it is too large Load Diff

View File

@ -5,11 +5,93 @@
// TOOD: Write our own HPACK implementation // TOOD: Write our own HPACK implementation
#include "hpack/HPacker.h" #include "hpack/HPacker.h"
#include <variant>
namespace drogon namespace drogon
{ {
namespace internal namespace internal
{ {
struct ByteStream;
struct OByteStream;
struct SettingsFrame
{
bool ack = false;
std::vector<std::pair<uint16_t, uint32_t>> settings;
static std::optional<SettingsFrame> parse(ByteStream &payload,
uint8_t flags);
bool serialize(OByteStream &stream, uint8_t &flags) const;
};
struct WindowUpdateFrame
{
uint32_t windowSizeIncrement = 0;
static std::optional<WindowUpdateFrame> parse(ByteStream &payload,
uint8_t flags);
bool serialize(OByteStream &stream, uint8_t &flags) const;
};
struct HeadersFrame
{
uint8_t padLength = 0;
bool exclusive = false;
uint32_t streamDependency = 0;
uint8_t weight = 0;
std::vector<uint8_t> headerBlockFragment;
bool endHeaders = false;
bool endStream = false;
static std::optional<HeadersFrame> parse(ByteStream &payload,
uint8_t flags);
bool serialize(OByteStream &stream, uint8_t &flags) const;
};
struct GoAwayFrame
{
uint32_t lastStreamId = 0;
uint32_t errorCode = 0;
std::vector<uint8_t> additionalDebugData;
static std::optional<GoAwayFrame> parse(ByteStream &payload, uint8_t flags);
bool serialize(OByteStream &stream, uint8_t &flags) const;
};
struct DataFrame
{
uint8_t padLength = 0;
std::vector<uint8_t> data;
bool endStream = false;
static std::optional<DataFrame> parse(ByteStream &payload, uint8_t flags);
bool serialize(OByteStream &stream, uint8_t &flags) const;
};
struct PingFrame
{
std::array<uint8_t, 8> opaqueData;
bool ack = false;
static std::optional<PingFrame> parse(ByteStream &payload, uint8_t flags);
bool serialize(OByteStream &stream, uint8_t &flags) const;
};
using H2Frame = std::variant<SettingsFrame,
WindowUpdateFrame,
HeadersFrame,
GoAwayFrame,
DataFrame,
PingFrame>;
enum class StreamState
{
ExpectingHeaders,
ExpectingContinuation,
ExpectingData,
Finished,
};
// Virtual stream that holds properties for the HTTP/2 stream // Virtual stream that holds properties for the HTTP/2 stream
// Defaults to stream 0 global properties // Defaults to stream 0 global properties
@ -17,11 +99,32 @@ struct H2Stream
{ {
HttpReqCallback callback; HttpReqCallback callback;
HttpResponseImplPtr response; HttpResponseImplPtr response;
HttpRequestPtr request;
trantor::MsgBuffer body; trantor::MsgBuffer body;
std::optional<size_t> contentLength;
int32_t streamId = 0;
StreamState state = StreamState::ExpectingHeaders;
}; };
} // namespace internal } // namespace internal
enum class StreamCloseErrorCode
{
NoError = 0x0,
ProtocolError = 0x1,
InternalError = 0x2,
FlowControlError = 0x3,
SettingsTimeout = 0x4,
StreamClosed = 0x5,
FrameSizeError = 0x6,
RefusedStream = 0x7,
Cancel = 0x8,
CompressionError = 0x9,
ConnectError = 0xa,
EnhanceYourCalm = 0xb,
InadequateSecurity = 0xc,
Http11Required = 0xd,
};
class Http2Transport : public HttpTransport class Http2Transport : public HttpTransport
{ {
private: private:
@ -42,9 +145,21 @@ class Http2Transport : public HttpTransport
size_t maxFrameSize = 16384; size_t maxFrameSize = 16384;
size_t avaliableWindowSize = 0; size_t avaliableWindowSize = 0;
// Configuration settings
const size_t windowIncreaseThreshold = 32768;
const size_t windowIncreaseSize = 10 * 1024 * 1024; // 10 MiB
// Set after server settings are received // Set after server settings are received
bool serverSettingsReceived = false; bool serverSettingsReceived = false;
std::vector<std::pair<HttpRequestPtr, HttpReqCallback>> bufferedRequests; std::queue<std::pair<HttpRequestPtr, HttpReqCallback>> bufferedRequests;
size_t avaliableWindow = 10 * 1024 * 1024; // 10 MiB
internal::H2Stream &createStream(int32_t streamId);
void streamFinished(internal::H2Stream &stream);
void streamFinished(int32_t streamId,
ReqResult result,
StreamCloseErrorCode errorCode,
std::string errorMsg = "");
int32_t nextStreamId() int32_t nextStreamId()
{ {
@ -78,6 +193,10 @@ class Http2Transport : public HttpTransport
} }
} }
void handleFrameForStream(const internal::H2Frame &frame,
int32_t streamId,
uint8_t flags);
public: public:
Http2Transport(trantor::TcpConnectionPtr connPtr, Http2Transport(trantor::TcpConnectionPtr connPtr,
size_t *bytesSent, size_t *bytesSent,
@ -99,10 +218,7 @@ class Http2Transport : public HttpTransport
"HTTP/2 handleConnectionClose not implemented"); "HTTP/2 handleConnectionClose not implemented");
} }
void onError(ReqResult result) override void onError(ReqResult result) override;
{
throw std::runtime_error("HTTP/2 onError not implemented");
}
protected: protected:
void onServerSettingsReceived(){}; void onServerSettingsReceived(){};

View File

@ -109,6 +109,9 @@ const char *HttpResponseImpl::versionString() const
case Version::kHttp11: case Version::kHttp11:
result = "HTTP/1.1"; result = "HTTP/1.1";
break; break;
case Version::kHttp2:
result = "HTTP/2";
break;
default: default:
break; break;