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>
|
||||
|
||||
std::set<uWS::HttpResponse<true> *> delayedResponses;
|
||||
std::set<uWS::HttpResponse<false> *> delayedResponses;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
@@ -56,13 +56,22 @@ int main(int argc, char **argv) {
|
||||
|
||||
}, 1000, 1000);
|
||||
|
||||
uWS::SSLApp({
|
||||
uWS::/*SSL*/App(/*{
|
||||
.key_file_name = "/home/alexhultman/uWebSockets/misc/ssl/key.pem",
|
||||
.cert_file_name = "/home/alexhultman/uWebSockets/misc/ssl/cert.pem",
|
||||
.dh_params_file_name = "/home/alexhultman/dhparams.pem",
|
||||
.passphrase = "1234"
|
||||
}).get("/", [](auto *res, auto *req) {
|
||||
}*/).get("/", [](auto *res, auto *req) {
|
||||
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) {
|
||||
/* This route streams back chunks of data in delayed fashion */
|
||||
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 */
|
||||
// todo: consider supporting nextLength = UNKNOWN as -1 (more but unknown size)
|
||||
int write(const char *src, int length, bool optionally = false, int nextLength = 0) {
|
||||
LoopData *loopData = getLoopData();
|
||||
|
||||
|
||||
+56
-29
@@ -138,8 +138,16 @@ public:
|
||||
httpResponseData->state |= HttpResponseData<SSL>::HTTP_WRITE_CALLED;
|
||||
if (length) {
|
||||
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?
|
||||
|
||||
// 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?
|
||||
auto [msg_more, chunk] = cb(0);
|
||||
// 1. pull a chunk
|
||||
// 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) {
|
||||
/* Rely on FIN to signal end if we do not pass any length */
|
||||
AsyncSocket<SSL>::write("Content-Length: ", 16);
|
||||
writeUnsigned(chunk.length());
|
||||
AsyncSocket<SSL>::write("\r\n", 2);
|
||||
// check for signals
|
||||
if (chunk.length() == 0) {
|
||||
if (chunk == HTTP_STREAM_FIN.second) {
|
||||
// flush and FIN
|
||||
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 */
|
||||
void write(std::string_view data, std::function<void(std::string_view)> cb = nullptr) {
|
||||
if (cb) {
|
||||
// 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
|
||||
|
||||
@@ -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());
|
||||
} else {
|
||||
// requires no extra alloc
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user