HttpResponse::internalEnd for end & tryEnd
This commit is contained in:
+91
-76
@@ -12,6 +12,9 @@ namespace uWS {
|
|||||||
/* Some pre-defined status constants to use with writeStatus */
|
/* Some pre-defined status constants to use with writeStatus */
|
||||||
const char *HTTP_200_OK = "200 OK";
|
const char *HTTP_200_OK = "200 OK";
|
||||||
|
|
||||||
|
/* The general timeout for HTTP sockets */
|
||||||
|
const int HTTP_TIMEOUT_S = 10;
|
||||||
|
|
||||||
template <bool SSL>
|
template <bool SSL>
|
||||||
struct HttpResponse : public AsyncSocket<SSL> {
|
struct HttpResponse : public AsyncSocket<SSL> {
|
||||||
typedef AsyncSocket<SSL> Super;
|
typedef AsyncSocket<SSL> Super;
|
||||||
@@ -38,10 +41,82 @@ private:
|
|||||||
Super::write(buf, length);
|
Super::write(buf, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns true on success, indicating that it might be feasible to write more data.
|
||||||
|
* Will start timeout if stream reaches totalSize or write failure. */
|
||||||
|
bool internalEnd(std::string_view data, int totalSize, bool optional) {
|
||||||
|
/* Write status if not already done */
|
||||||
|
writeStatus(HTTP_200_OK);
|
||||||
|
|
||||||
|
/* If no total size given then assume this chunk is everything */
|
||||||
|
if (!totalSize) {
|
||||||
|
totalSize = data.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponseData<SSL> *httpResponseData = getHttpResponseData();
|
||||||
|
if (httpResponseData->state & HttpResponseData<SSL>::HTTP_WRITE_CALLED) {
|
||||||
|
|
||||||
|
// we do not listen to optional here!
|
||||||
|
|
||||||
|
/* Do not allow sending 0 chunk here */
|
||||||
|
if (data.length()) {
|
||||||
|
Super::write("\r\n", 2);
|
||||||
|
writeUnsignedHex(data.length());
|
||||||
|
Super::write("\r\n", 2);
|
||||||
|
|
||||||
|
// should be optional
|
||||||
|
Super::write(data.data(), data.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Terminating 0 chunk */
|
||||||
|
Super::write("\r\n0\r\n\r\n", 7);
|
||||||
|
|
||||||
|
// what about timeout here!?
|
||||||
|
|
||||||
|
// always start timeout here!
|
||||||
|
Super::timeout(HTTP_TIMEOUT_S);
|
||||||
|
|
||||||
|
// unclear about this path really
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/* Write content-length on first call */
|
||||||
|
if (!(httpResponseData->state & HttpResponseData<SSL>::HTTP_END_CALLED)) {
|
||||||
|
/* We have a known send size */
|
||||||
|
Super::write("Content-Length: ", 16);
|
||||||
|
writeUnsigned(totalSize);
|
||||||
|
Super::write("\r\n\r\n", 4);
|
||||||
|
|
||||||
|
/* Mark end called */
|
||||||
|
httpResponseData->state |= HttpResponseData<SSL>::HTTP_END_CALLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Even if we supply no new data to write, its failed boolean is useful to know
|
||||||
|
* if it failed to drain any prior failed header writes */
|
||||||
|
|
||||||
|
/* Write as much as possible without causing backpressure */
|
||||||
|
auto [written, failed] = Super::write(data.data(), data.length(), optional);
|
||||||
|
httpResponseData->offset += written;
|
||||||
|
|
||||||
|
/* Success is when we wrote the entire thing without any failures */
|
||||||
|
bool success = written == data.length() && !failed;
|
||||||
|
|
||||||
|
/* If we are now at the end, start a timeout. Also start a timeout if we failed. */
|
||||||
|
if (!success || httpResponseData->offset == totalSize) {
|
||||||
|
Super::timeout(HTTP_TIMEOUT_S);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/* Immediately terminate this Http response */
|
||||||
using Super::close;
|
using Super::close;
|
||||||
|
|
||||||
|
/* Note: Headers are not checked in regards to timeout.
|
||||||
|
* We only check when you actively push data or end the request */
|
||||||
|
|
||||||
/* Write the HTTP status */
|
/* Write the HTTP status */
|
||||||
HttpResponse *writeStatus(std::string_view status) {
|
HttpResponse *writeStatus(std::string_view status) {
|
||||||
HttpResponseData<SSL> *httpResponseData = getHttpResponseData();
|
HttpResponseData<SSL> *httpResponseData = getHttpResponseData();
|
||||||
@@ -78,42 +153,29 @@ public:
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* End the response with an optional data chunk */
|
/* End the response with an optional data chunk. Always starts a timeout. */
|
||||||
void end(std::string_view data = {}) {
|
void end(std::string_view data = {}) {
|
||||||
writeStatus(HTTP_200_OK);
|
internalEnd(data, data.length(), false);
|
||||||
|
}
|
||||||
|
|
||||||
HttpResponseData<SSL> *httpResponseData = getHttpResponseData();
|
/* Try and end the response. Returns true on success. Starts a timeout in some cases. */
|
||||||
|
bool tryEnd(std::string_view data, int totalSize = 0) {
|
||||||
|
bool succeeded = internalEnd(data, totalSize, true);
|
||||||
|
|
||||||
if (httpResponseData->state & HttpResponseData<SSL>::HTTP_WRITE_CALLED) {
|
std::cout << "tryEnd with size " << data.length() << " returned " << succeeded << std::endl;
|
||||||
/* Do not allow sending 0 chunk here */
|
|
||||||
if (data.length()) {
|
|
||||||
Super::write("\r\n", 2);
|
|
||||||
writeUnsignedHex(data.length());
|
|
||||||
Super::write("\r\n", 2);
|
|
||||||
Super::write(data.data(), data.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Terminating 0 chunk */
|
return succeeded;
|
||||||
Super::write("\r\n0\r\n\r\n", 7);
|
|
||||||
} else {
|
|
||||||
/* We have a known send size */
|
|
||||||
Super::write("Content-Length: ", 16);
|
|
||||||
writeUnsigned(data.length());
|
|
||||||
Super::write("\r\n\r\n", 4);
|
|
||||||
|
|
||||||
Super::write(data.data(), data.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: calling end should start a timeout of the socket!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write parts of the response in chunking fashion */
|
/* Write parts of the response in chunking fashion */
|
||||||
|
// fic this up and add tryWrite (will require more state!)
|
||||||
bool write(std::string_view data) {
|
bool write(std::string_view data) {
|
||||||
writeStatus(HTTP_200_OK);
|
writeStatus(HTTP_200_OK);
|
||||||
|
|
||||||
/* Do not allow sending 0 chunks, they mark end of response */
|
/* Do not allow sending 0 chunks, they mark end of response */
|
||||||
if (!data.length()) {
|
if (!data.length()) {
|
||||||
return true; // are we corked still?
|
/* If you called us, then according to you it was fine to call us so it's fine to still call us */
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponseData<SSL> *httpResponseData = getHttpResponseData();
|
HttpResponseData<SSL> *httpResponseData = getHttpResponseData();
|
||||||
@@ -126,64 +188,17 @@ public:
|
|||||||
Super::write("\r\n", 2);
|
Super::write("\r\n", 2);
|
||||||
writeUnsignedHex(data.length());
|
writeUnsignedHex(data.length());
|
||||||
Super::write("\r\n", 2);
|
Super::write("\r\n", 2);
|
||||||
|
|
||||||
|
// this should essentially return what we want to return from here!
|
||||||
Super::write(data.data(), data.length());
|
Super::write(data.data(), data.length());
|
||||||
|
|
||||||
|
// we want write to return whether the user may call write again, basically if we are polling for writable
|
||||||
|
|
||||||
// are we corked still?
|
// are we corked still?
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: share this code in a function
|
/* Get the current byte write offset for this Http response */
|
||||||
bool tryEnd(std::string_view data, int totalSize = 0) {
|
|
||||||
/* Write status if not already done */
|
|
||||||
writeStatus(HTTP_200_OK);
|
|
||||||
|
|
||||||
/* If no total size given then assume this chunk is everything */
|
|
||||||
if (!totalSize) {
|
|
||||||
totalSize = data.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpResponseData<SSL> *httpResponseData = getHttpResponseData();
|
|
||||||
|
|
||||||
if (httpResponseData->state & HttpResponseData<SSL>::HTTP_WRITE_CALLED) {
|
|
||||||
/* Do not allow sending 0 chunk here */
|
|
||||||
if (data.length()) {
|
|
||||||
Super::write("\r\n", 2);
|
|
||||||
writeUnsignedHex(data.length());
|
|
||||||
Super::write("\r\n", 2);
|
|
||||||
|
|
||||||
// should be optional
|
|
||||||
Super::write(data.data(), data.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Terminating 0 chunk */
|
|
||||||
Super::write("\r\n0\r\n\r\n", 7);
|
|
||||||
} else {
|
|
||||||
// if not already ended! should we call tryWrite after this? we need an extra flag! end called!
|
|
||||||
|
|
||||||
if (!(httpResponseData->state & HttpResponseData<SSL>::HTTP_END_CALLED)) {
|
|
||||||
/* We have a known send size */
|
|
||||||
Super::write("Content-Length: ", 16);
|
|
||||||
writeUnsigned(/*data.length()*/totalSize);
|
|
||||||
Super::write("\r\n\r\n", 4);
|
|
||||||
|
|
||||||
/* Mark end called */
|
|
||||||
httpResponseData->state |= HttpResponseData<SSL>::HTTP_END_CALLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write as much as possible without causing backpressure */
|
|
||||||
int written = Super::write(data.data(), data.length(), true);
|
|
||||||
|
|
||||||
httpResponseData->offset += written;
|
|
||||||
//std::cout << "Offset is now: " << httpResponseData->offset << std::endl;
|
|
||||||
|
|
||||||
return written == data.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
// this path is completely wrong!
|
|
||||||
//std::cout << "tryEnd returning " << (httpResponseData->offset == /*totalSize*/ data.length()) << std::endl;
|
|
||||||
return httpResponseData->offset == /*totalSize*/ data.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
int getWriteOffset() {
|
int getWriteOffset() {
|
||||||
HttpResponseData<SSL> *httpResponseData = getHttpResponseData();
|
HttpResponseData<SSL> *httpResponseData = getHttpResponseData();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user