Giter Club home page Giter Club logo

Comments (11)

kartikk221 avatar kartikk221 commented on May 24, 2024 1

Streaming without the total source size has been added in 5.1.1. It uses the write() method effectively using chunked transfer method to serve data. The only downside to this is that no content-length is specified and the write() method does not handle backpressure so there is no way to guarantee that all sent data was received. You still have the option to use handled transfer by specifying a total size.

from hyper-express.

kartikk221 avatar kartikk221 commented on May 24, 2024 1

Interesting, will push this fix in an update soon and still keep tryEnd as an option so users who want the content-length header for whatever reason can specify a total_size.

from hyper-express.

kartikk221 avatar kartikk221 commented on May 24, 2024

The stream method was implemented based on the official streaming example from uWebsockets.js repository (See https://github.com/uNetworking/uWebSockets.js/blob/master/examples/VideoStreamer.js).

Under the hood, the Response.stream() method uses uWS.Response.tryEnd(chunk, totalSize) method which requires a totalSize parameter (I am assuming this has to do with how uWebsockets serves chunks and responds to backpressure under the hood).

The only somewhat hacky way I can think of implementing streaming with uWebsockets.js without knowing the total size would be to provide an artificially high totalSize to the tryEnd method until the Readable.on('end') event is fired upon which we serve an empty chunk and the totalSize is specified as the total amount of bytes sent which can be tracked using a private variable. Will look into implementing this now and see if its viable.

from hyper-express.

snowbldr avatar snowbldr commented on May 24, 2024

Nodejs streams will handle backpressure too (https://nodejs.org/en/docs/guides/backpressuring-in-streams/), so using the write method instead of tryEnd of the uws response wrapped as a Writable would probably be sufficient for most use cases. At worst the pressurized? data will get cached and use up memory on the machine, which is likely still better than having the entire response in memory. It's possible that the write method of the uWS response does backpressure handling too, honestly I don't know.

I believe that totalSize is used to set the content-length header, which would get written on the first write, meaning the client would likely fail because the response was the wrong size.

But I see that the write method on the hyper-express response does write the headers correctly as of 5.1, so I can wrap it in a writable myself as a work around. It would be nice if this framework supported it natively though.

Btw my use case is streaming large amounts of data out of a database, so I can't possibly know the size of it without caching the entire thing in memory or causing an extra load on the database to calculate the size.

from hyper-express.

kartikk221 avatar kartikk221 commented on May 24, 2024

No, so the way Node.js handles backpressure is based on when you trigger the callback from the write handler. The thing is the uWS.write() method does not return a Boolean value to signify backpressure which means we have no choice but to call the callback from the write handler after making the uWS.write() call. With this we will write all chunks every time even if some chunks failed with backpressure. This is precisely the reason why the example from uWebsockets.js repository uses tryEnd for streaming the video file. I am actually not sure what the purpose of the write method it writes body chunks without returning any signal to handle backpressure.

Code Example:

const stream = new Writable({
   write: (chunk, encoding, callback) => {
      response.write(chunk); // This method does not return a Boolean to tell us whether this chunk was sent or not
      callback(); // We have no choice but to call the callback thus Node will continue streaming even though we may not be sending any chunks
   }
})

from hyper-express.

snowbldr avatar snowbldr commented on May 24, 2024

No, so the way Node.js handles backpressure is based on when you trigger the callback from the write handler. The thing is the uWS.write() method does not return a Boolean value to signify backpressure which means we have no choice but to call the callback from the write handler after making the uWS.write() call. With this we will write all chunks every time even if some chunks failed with backpressure. This is precisely the reason why the example from uWebsockets.js repository uses tryEnd for streaming the video file. I am actually not sure what the purpose of the write method it writes body chunks without returning any signal to handle backpressure.

Code Example:

const stream = new Writable({
   write: (chunk, encoding, callback) => {
      response.write(chunk); // This method does not return a Boolean to tell us whether this chunk was sent or not
      callback(); // We have no choice but to call the callback thus Node will continue streaming even though we may not be sending any chunks
   }
})

Ah, good point. Perhaps uWS will just not return from the write method if it's experiencing too much backpressure... Thanks for the super quick updates :D

from hyper-express.

snowbldr avatar snowbldr commented on May 24, 2024

Actually, it looks like write does return a boolean. I'm super noob at c++ so I might be reading this wrong...

https://github.com/uNetworking/uWebSockets.js/blob/c70d2e6d2d923183885e991c6cb4189419b521a5/src/HttpResponseWrapper.h#L259

template <bool SSL>
    static void res_write(const FunctionCallbackInfo<Value> &args) {
        Isolate *isolate = args.GetIsolate();
        auto *res = getHttpResponse<SSL>(args);
        if (res) {
            NativeString data(args.GetIsolate(), args[0]);
            if (data.isInvalid(args)) {
                return;
            }
            bool ok = res->write(data.getString());

            args.GetReturnValue().Set(Boolean::New(isolate, ok));
        }
    }

That isolate thing handles js context if I'm understanding correctly, and the GetReturnValue gets passed the ok boolean from write.

from hyper-express.

snowbldr avatar snowbldr commented on May 24, 2024

The docs disagree though... Weird....
https://unetworking.github.io/uWebSockets.js/generated/interfaces/HttpResponse.html#write

from hyper-express.

snowbldr avatar snowbldr commented on May 24, 2024

Opened an issue with uWebSockets.js uNetworking/uWebSockets.js#638

from hyper-express.

snowbldr avatar snowbldr commented on May 24, 2024

Heh, it does return a boolean. uNetworking/uWebSockets.js#638 (comment)

from hyper-express.

snowbldr avatar snowbldr commented on May 24, 2024

https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2

A sender MUST NOT send a Content-Length header field in any message
that contains a Transfer-Encoding header field.

The only downside to this is that no content-length is specified

Seems like this is not an issue.

from hyper-express.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.