Support responses with no specified length
This commit is contained in:
@@ -37,7 +37,7 @@ std::string_view getFile(std::string_view file) {
|
|||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
std::set<uWS::HttpResponse<true> *> delayedResponses;
|
std::set<uWS::HttpResponse<false> *> delayedResponses;
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
|
||||||
@@ -56,13 +56,22 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
}, 1000, 1000);
|
}, 1000, 1000);
|
||||||
|
|
||||||
uWS::SSLApp({
|
uWS::/*SSL*/App(/*{
|
||||||
.key_file_name = "/home/alexhultman/uWebSockets/misc/ssl/key.pem",
|
.key_file_name = "/home/alexhultman/uWebSockets/misc/ssl/key.pem",
|
||||||
.cert_file_name = "/home/alexhultman/uWebSockets/misc/ssl/cert.pem",
|
.cert_file_name = "/home/alexhultman/uWebSockets/misc/ssl/cert.pem",
|
||||||
.dh_params_file_name = "/home/alexhultman/dhparams.pem",
|
.dh_params_file_name = "/home/alexhultman/dhparams.pem",
|
||||||
.passphrase = "1234"
|
.passphrase = "1234"
|
||||||
}).get("/", [](auto *res, auto *req) {
|
}*/).get("/", [](auto *res, auto *req) {
|
||||||
res->writeStatus(uWS::HTTP_200_OK)->write("Hello world!");
|
res->writeStatus(uWS::HTTP_200_OK)->write("Hello world!");
|
||||||
|
}).get("/endless", [](auto *res, auto *req) {
|
||||||
|
/* This route doesn't specify any length and only returns 1 char per chunk */
|
||||||
|
res->write([](int offset) {
|
||||||
|
std::string_view data("<html><body><h1>Hello, world</h1></body></html>");
|
||||||
|
if (offset < data.length()) {
|
||||||
|
return std::make_pair<bool, std::string_view>(offset < data.length() - 1, data.substr(offset, 1));
|
||||||
|
}
|
||||||
|
return uWS::HTTP_STREAM_FIN;
|
||||||
|
});
|
||||||
}).get("/delayed", [](auto *res, auto *req) {
|
}).get("/delayed", [](auto *res, auto *req) {
|
||||||
/* This route streams back chunks of data in delayed fashion */
|
/* This route streams back chunks of data in delayed fashion */
|
||||||
res->writeStatus(uWS::HTTP_200_OK)->write([res](int offset) {
|
res->writeStatus(uWS::HTTP_200_OK)->write([res](int offset) {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Write in three levels of prioritization: cork-buffer, syscall, socket-buffer */
|
/* Write in three levels of prioritization: cork-buffer, syscall, socket-buffer */
|
||||||
|
// todo: consider supporting nextLength = UNKNOWN as -1 (more but unknown size)
|
||||||
int write(const char *src, int length, bool optionally = false, int nextLength = 0) {
|
int write(const char *src, int length, bool optionally = false, int nextLength = 0) {
|
||||||
LoopData *loopData = getLoopData();
|
LoopData *loopData = getLoopData();
|
||||||
|
|
||||||
|
|||||||
+56
-29
@@ -138,8 +138,16 @@ public:
|
|||||||
httpResponseData->state |= HttpResponseData<SSL>::HTTP_WRITE_CALLED;
|
httpResponseData->state |= HttpResponseData<SSL>::HTTP_WRITE_CALLED;
|
||||||
if (length) {
|
if (length) {
|
||||||
httpResponseData->state |= HttpResponseData<SSL>::HTTP_KNOWN_STREAM_OUT_SIZE;
|
httpResponseData->state |= HttpResponseData<SSL>::HTTP_KNOWN_STREAM_OUT_SIZE;
|
||||||
|
|
||||||
|
/* Rely on FIN to signal end if we do not pass any length */
|
||||||
|
AsyncSocket<SSL>::write("Content-Length: ", 16);
|
||||||
|
writeUnsigned(length);
|
||||||
|
AsyncSocket<SSL>::write("\r\n", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* HTTP body separator */
|
||||||
|
AsyncSocket<SSL>::write("\r\n", 2);
|
||||||
|
|
||||||
// int tail, int head. tail is what has been sent off, head is where we read from. they can differ - does it matter?
|
// int tail, int head. tail is what has been sent off, head is where we read from. they can differ - does it matter?
|
||||||
|
|
||||||
// just do resume() and pause() to throttle onwritable calling
|
// just do resume() and pause() to throttle onwritable calling
|
||||||
@@ -156,45 +164,64 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// flow below should be:
|
||||||
|
|
||||||
// this check should be its own shared function?
|
// 1. pull a chunk
|
||||||
auto [msg_more, chunk] = cb(0);
|
// 2. check for signals in this chunk and break appropriately
|
||||||
|
// 3. if no signals, send this chunk
|
||||||
|
// 4. if sent fine but still has data to write, goto 1
|
||||||
|
// 5. if sent less than expected, set offset and callback to httpResponseData and exit
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
while (true) {
|
||||||
|
// pull chunk
|
||||||
|
auto [msg_more, chunk] = cb(offset);
|
||||||
|
|
||||||
if (length) {
|
// check for signals
|
||||||
/* Rely on FIN to signal end if we do not pass any length */
|
if (chunk.length() == 0) {
|
||||||
AsyncSocket<SSL>::write("Content-Length: ", 16);
|
if (chunk == HTTP_STREAM_FIN.second) {
|
||||||
writeUnsigned(chunk.length());
|
// flush and FIN
|
||||||
AsyncSocket<SSL>::write("\r\n", 2);
|
AsyncSocket<SSL>::uncork();
|
||||||
|
us_socket_shutdown((us_socket *) this);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
std::cout << "Paused stream!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip sending optional, yet keep refusing to call onWritable while in paused mode (SSL may poll for writable!)
|
||||||
|
// we thus need a status: paused to check for before requesting more data (also check for this in resume call!)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send this chunk
|
||||||
|
int written = AsyncSocket<SSL>::write(chunk.data(), chunk.length(), true);
|
||||||
|
|
||||||
|
// if we sent less than expected, always end
|
||||||
|
if (written < chunk.length()) {
|
||||||
|
std::cout << "HttpResponse::write failed to write everything" << std::endl;
|
||||||
|
httpResponseData->offset = offset + written;
|
||||||
|
httpResponseData->outStream = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have more to send or are still not FINed
|
||||||
|
if (length == 0 || offset + written < length) {
|
||||||
|
offset += written;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// when do we get here?
|
||||||
|
std::cout << "How did we get here?" << std::endl;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* HTTP body separator */
|
|
||||||
AsyncSocket<SSL>::write("\r\n", 2);
|
|
||||||
|
|
||||||
/* Did the user pause ? */
|
|
||||||
if (chunk.length() == 0) {
|
|
||||||
|
|
||||||
// skip sending optional, yet keep refusing to call onWritable while in paused mode (SSL may poll for writable!)
|
|
||||||
|
|
||||||
// we thus need a status: paused to check for before requesting more data (also check for this in resume call!)
|
|
||||||
|
|
||||||
std::cout << "Paused stream!" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write as much as possible, optionally */
|
|
||||||
if (int written; (written = AsyncSocket<SSL>::write(chunk.data(), chunk.length(), true)) < length) {
|
|
||||||
std::cout << "HttpResponse::write failed to write everything" << std::endl;
|
|
||||||
httpResponseData->offset = written;
|
|
||||||
httpResponseData->outStream = cb;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convenience function for static data */
|
/* Convenience function for static data */
|
||||||
void write(std::string_view data, std::function<void(std::string_view)> cb = nullptr) {
|
void write(std::string_view data, std::function<void(std::string_view)> cb = nullptr) {
|
||||||
if (cb) {
|
if (cb) {
|
||||||
// todo: think about how the stream will signal done (streams API challenge overall)
|
// todo: think about how the stream will signal done (streams API challenge overall)
|
||||||
write([data](int offset, int length) {
|
write([data](int offset) {
|
||||||
|
|
||||||
// if offset == length then we know it is end
|
// if offset == length then we know it is end
|
||||||
|
|
||||||
@@ -202,12 +229,12 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
return {false, data.substr(offset)};
|
return std::make_pair<bool, std::string_view>(false, data.substr(offset));//{false, data.substr(offset)};
|
||||||
}, data.length());
|
}, data.length());
|
||||||
} else {
|
} else {
|
||||||
// requires no extra alloc
|
// requires no extra alloc
|
||||||
write([data](int offset) {
|
write([data](int offset) {
|
||||||
return {false, data.substr(offset)};
|
return std::make_pair<bool, std::string_view>(false, data.substr(offset));//{false, data.substr(offset)};
|
||||||
}, data.length());
|
}, data.length());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user