diff --git a/main.cpp b/main.cpp
index 8daa033..ec6b240 100644
--- a/main.cpp
+++ b/main.cpp
@@ -12,64 +12,107 @@ int main(int argc, char **argv) {
.dh_params_file_name = "/home/alexhultman/dhparams.pem",
.passphrase = "1234"
}*/).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("
Hello, world
");
- if (offset < data.length()) {
- return std::make_pair(offset < data.length() - 1, data.substr(offset, 1));
- }
- return uWS::HTTP_STREAM_FIN;
- });
+
+ res->writeStatus(uWS::HTTP_200_OK);
+ res->writeHeader("Content-Type", "text/html;charset=utf-8");
+
+ res->write("Hallå!
Din user-agent är: ");
+ res->end(req->getHeader("user-agent"));
+
+// }).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("Hello, world
");
+// if (offset < data.length()) {
+// return std::make_pair(offset < data.length() - 1, data.substr(offset, 1));
+// }
+// return uWS::HTTP_STREAM_FIN;
+// });
}).get("/async/sintel.mkv", [](auto *res, auto *req) {
// res->write(asyncFileReader.stream(res))
// asyncFileReader.getAsStream()
+
+ // res->write(data, length) -> bool (identical to nodejs)
+ // res->tryWrite(data, length) -> int (what we will use mostly)
+ // both operate like uSockets, they are paused by default and will trigger onWritable if set
+
+ // res->onWritable(lambda) -> the same as "drain" even in Node.js, call tryWrite here, or do nothing to hang/pause
+
+ // resume is gone, it will resume automatically as needed, just like uSockets do automatically
+
+ // res will be somewhat like AsyncSocket but with certain middle functions for formatting the data (think Content-Length and Chunked)
+
+
+ // res->onAborted(lambda) will be called when closed, for now all events lie on the res
+
+ // res->onData() will probably be the same?
+
+ //res->writeStatus(uWS::HTTP_200_OK)->end("Hello Sintel!");
+
+ // om vi håller headers i en buffer och data i en annan?
+
+ // om end eller tryEnd inte kommer före write swappar den till chunked!
+ res->write("Hallå! Din user-agent är: "); // -> skriver till cork-buffern "Transfer-Encoding: chunked" och ett chunk
+ res->end(req->getHeader("user-agent"));
+
+ // write före end går över till chunked, från där kan man extenda till SSE men websockets är bättre format (binärt, mindre)
+
+ // hur fungerar detta med server-sent-events?
+
+
+ // end efter write blir ett noll-segment i chunked!
+
+ /*if (!res->tryEnd()) {
+
+ }*/
+
+
+
/* 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) {
- /* Handle broken stream */
- if (offset == -1) {
- std::cout << "Stream was closed by peer!" << std::endl;
- return uWS::HTTP_STREAM_IGNORE;
- }
+// /* Handle broken stream */
+// if (offset == -1) {
+// std::cout << "Stream was closed by peer!" << std::endl;
+// return uWS::HTTP_STREAM_IGNORE;
+// }
- /* Peek from cache */
- std::string_view chunk = asyncFileReader.peek(offset);
- if (chunk.length()) {
- /* We had parts of this file cached already */
- return std::pair(false, chunk);
- } else {
+// /* Peek from cache */
+// std::string_view chunk = asyncFileReader.peek(offset);
+// if (chunk.length()) {
+// /* We had parts of this file cached already */
+// return std::pair(false, chunk);
+// } else {
- //std::string_view outerChunk;
+// //std::string_view outerChunk;
- /* We had nothing readily available right now, request async chunk and pause the stream until we have */
- asyncFileReader.request(offset, [res](std::string_view chunk) {
+// /* We had nothing readily available right now, request async chunk and pause the stream until we have */
+// asyncFileReader.request(offset, [res](std::string_view chunk) {
- //std::cout << "We came here!" << std::endl;
+// //std::cout << "We came here!" << std::endl;
- /* We were aborted */
- if (!chunk.length()) {
- std::cout << "Async File Read request was aborted!" << std::endl;
- // close the socket here?
- // we need a way to NOT resume a paused socket! essentially close!
- } else {
- /* We finally got the data, resume stream with this chunk */
- res->resume(chunk);
- }
- });
+// /* We were aborted */
+// if (!chunk.length()) {
+// std::cout << "Async File Read request was aborted!" << std::endl;
+// // close the socket here?
+// // we need a way to NOT resume a paused socket! essentially close!
+// } else {
+// /* We finally got the data, resume stream with this chunk */
+// res->resume(chunk);
+// }
+// });
- //std::cout << "Returning chunk of size: " << outerChunk.length() << std::endl;
- //return std::pair(false, outerChunk);
+// //std::cout << "Returning chunk of size: " << outerChunk.length() << std::endl;
+// //return std::pair(false, outerChunk);
- // what if we resumed before we paused! we cannot do that!
+// // what if we resumed before we paused! we cannot do that!
- //std::cout << "PAusing stream out due to empty cache!" << std::endl;
- return uWS::HTTP_STREAM_PAUSE;
- }
- }, asyncFileReader.getFileSize());
+// //std::cout << "PAusing stream out due to empty cache!" << std::endl;
+// return uWS::HTTP_STREAM_PAUSE;
+// }
+// }, asyncFileReader.getFileSize());
}).listen(3000, [](auto *token) {
if (token) {
std::cout << "Listening on port " << 3000 << std::endl;
diff --git a/src/HttpResponse.h b/src/HttpResponse.h
index 62187ac..8c81712 100644
--- a/src/HttpResponse.h
+++ b/src/HttpResponse.h
@@ -11,15 +11,6 @@ namespace uWS {
/* Some pre-defined status constants to use with writeStatus */
const char *HTTP_200_OK = "200 OK";
-/* Return this from a stream callback to signal pause */
-const std::pair HTTP_STREAM_PAUSE = {false, std::string_view(nullptr, 0)};
-
-/* Return this from a stream callback to signal FIN */
-const std::pair HTTP_STREAM_FIN = {false, std::string_view((const char *) 1, 0)};
-
-/* Nobody cares what value this one has */
-const auto HTTP_STREAM_IGNORE = HTTP_STREAM_FIN;
-
template
struct HttpResponse : public AsyncSocket {
private:
@@ -27,6 +18,33 @@ private:
return (HttpResponseData *) AsyncSocket::getExt();
}
+ int u32toaHex(uint32_t value, char *dst) {
+ char palette[] = "0123456789abcdef";
+ char temp[10];
+ char *p = temp;
+ do {
+ *p++ = palette[value % 16];
+ value /= 16;
+ } while (value > 0);
+
+ int ret = p - temp;
+
+ do {
+ *dst++ = *--p;
+ } while (p != temp);
+
+ return ret;
+ }
+
+ /* Write an unsigned 32-bit integer in hex */
+ void writeUnsignedHex(unsigned int value) {
+ char buf[10];
+ int length = u32toaHex(value, buf);
+
+ /* For now we do this copy */
+ AsyncSocket::write(buf, length);
+ }
+
int u32toa(uint32_t value, char *dst) {
char temp[10];
char *p = temp;
@@ -90,58 +108,66 @@ public:
return this;
}
- /* Resume response streaming as far as possible */
- void resume(std::string_view chunk = {}) {
+ /* End the response with an optional data chunk */
+ void end(std::string_view data = {}) {
HttpResponseData *httpResponseData = getHttpResponseData();
- /* Do nothing if not even paused */
- if (!(httpResponseData->state & HttpResponseData::HTTP_PAUSED_STREAM_OUT)) {
- std::cout << "Resue called but we are not even in paused state!" << std::endl;
- return;
+ if (httpResponseData->state & HttpResponseData::HTTP_WRITE_CALLED) {
+ /* Do not allow sending 0 chunk here */
+ if (data.length()) {
+ AsyncSocket::write("\r\n", 2);
+ writeUnsignedHex(data.length());
+ AsyncSocket::write("\r\n", 2);
+ AsyncSocket::write(data.data(), data.length());
+ }
+
+ /* Terminating 0 chunk */
+ AsyncSocket::write("\r\n0\r\n\r\n", 7);
+ } else {
+ /* We have a known send size */
+ AsyncSocket::write("Content-Length: ", 16);
+ writeUnsigned(data.length());
+ AsyncSocket::write("\r\n\r\n", 4);
+
+ AsyncSocket::write(data.data(), data.length());
}
-
- //std::cout << "Resume called and we really are paused" << std::endl;
-
- /* Remove paused status */
- httpResponseData->state &= ~HttpResponseData::HTTP_PAUSED_STREAM_OUT;
-
- /*if (chunk.length()) {
- int written = AsyncSocket::write(chunk.data(), chunk.length(), true);
-
- if (written == chunk.length()) {
- // pull a new chunk from the callback (basically call onWritable)
- std::cout << "Wrote everything off!" << std::endl;
- }
- }*/
-
- AsyncSocket *asyncSocket = this;
-
- // again, this path is shared with onwritable, write and here!
- while (true) {
- auto [msg_more, chunk] = httpResponseData->outStream(httpResponseData->offset);
-
- // break on pause!
- if (chunk.length() == 0) {
- //std::cout << "Resume paused!" << std::endl;
- httpResponseData->state |= HttpResponseData::HTTP_PAUSED_STREAM_OUT;
- break;
- }
-
- int written = asyncSocket->mergeDrain(chunk);
- httpResponseData->offset += written;
- // this is not correct, we can reach the end!
- if (written < chunk.length()) {
- break;
- }
- }
-
-
-
- // no, basically just write this off and if all written, call streamOut callback
}
+ /* Write parts of the response in chunking fashion */
+ bool write(std::string_view data) {
+ /* Do not allow sending 0 chunks, they mark end of response */
+ if (!data.length()) {
+ return true; // are we corked still?
+ }
+
+ HttpResponseData *httpResponseData = getHttpResponseData();
+
+ if (!(httpResponseData->state & HttpResponseData::HTTP_WRITE_CALLED)) {
+ writeHeader("Transfer-Encoding", "chunked");
+ httpResponseData->state |= HttpResponseData::HTTP_WRITE_CALLED;
+ }
+
+ AsyncSocket::write("\r\n", 2);
+ writeUnsignedHex(data.length());
+ AsyncSocket::write("\r\n", 2);
+ AsyncSocket::write(data.data(), data.length());
+
+ // are we corked still?
+ return true;
+ }
+
+ // we really want tryEnd(data) integer to try and stream something with known size
+
+ // write/tryWrite called first should enter into chunked?
+
+ // tryWrite(char *, length) int
+
+ // write(char *, length) bool
+
+
+
/* Attach an output stream function. Chunks may be read more than once. Negative offset mean broken stream */
- void write(std::function(int)> cb, int length = 0) {
+ void writeOldRemoveMe(std::function(int)> cb, int length = 0) {
HttpResponseData *httpResponseData = getHttpResponseData();
/* Do not allow write if already called */
@@ -209,28 +235,6 @@ public:
}
}
- /* Convenience function for static data */
- void write(std::string_view data, std::function cb = nullptr) {
- if (cb) {
- // todo: think about how the stream will signal done (streams API challenge overall)
- write([data](int offset) {
-
- // if offset == length then we know it is end
-
- // what if we want to return both fin and data? can't do that
-
-
-
- return std::make_pair(false, data.substr(offset));//{false, data.substr(offset)};
- }, data.length());
- } else {
- // requires no extra alloc
- write([data](int offset) {
- return std::make_pair(false, data.substr(offset));//{false, data.substr(offset)};
- }, data.length());
- }
- }
-
/* Attach a read handler for data sent. Will be called with a chunk of size 0 when FIN */
void read(std::function handler) {
HttpResponseData *data = getHttpResponseData();