Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Response with content provider is not initiating response until first byte is provided #2029

Open
cschreib-ibex opened this issue Jan 24, 2025 · 0 comments

Comments

@cschreib-ibex
Copy link

I need to create a route that serves a fairly large file, of size initially unknown. For practical reasons, I am not able to generate this file as a stream; it gets generated in a single function call, and this takes several seconds. I am calling this route on a web page behind a "Download" button, and I would like the user to be immediately prompted for where to save the file when they click this button. Unfortunately, whatever I do, the "Save As..." prompt only appears after a few seconds, waiting for my slow function to finish executing to produce the whole file.

I first tried using the content provider like so:

response.set_content_provider("application/zip", [](std::size_t offset, DataSink& sink) {
    const auto buffer = my_slow_function();
    sink.write(buffer.data(), buffer.size());
    sink.done();
    return true;
});

I thought that this would allow cpp-httplib to initiate the response, write the headers etc..., and start sending this initial data to the client which would prompt the "Save As..." dialog, and only then call my slow function. But no, I have to wait until my function finishes before the "Save As..." dialog appears.

After running some tests, I found that the "Save As..." dialog appears as soon as the content provider has written at least one byte of data to the sink:

response.set_content_provider("application/zip", [](std::size_t offset, DataSink& sink) {
    // Send a dummy char
    char c = 0;
    sink.write(&c, 1);

    // Send the real content
    const auto buffer = my_slow_function();
    sink.write(buffer.data(), buffer.size());
    sink.done();
    return true;
});

Now... I can actually use this as a workaround, because the file I am sending is always a zip file which starts with the same 4 bytes, so a workaround (specifically for zips) is:

response.set_content_provider("application/zip", [](std::size_t offset, DataSink& sink) {
    // Send first 4 bytes of zip header
    sink.write("PK\x03\x04", 4);

    // Send the real content, minus first 4 bytes which we already sent
    const auto buffer = my_slow_function();
    sink.write(buffer.data() + 4, buffer.size() - 4);
    sink.done();
    return true;
});

But this is inelegant (what if my_slow_function() returns another archive type some day?), and cannot be applied in a generic way.

Is there a way to somehow force cpp-httplib to send the response headers before the first sink.write() call?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant