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

Request to HTTP/2 server is not handled on("end") or on("close"), and behave differently from HTTP/1 #33537

Closed
nwtgck opened this issue May 24, 2020 · 4 comments
Labels
http2 Issues or PRs related to the http2 subsystem.

Comments

@nwtgck
Copy link

nwtgck commented May 24, 2020

  • Version: v12.16.3, v14.3.0
  • Platform: Darwin **** 18.7.0 Darwin Kernel Version 18.7.0: Mon Feb 10 21:08:45 PST 2020; root:xnu-4903.278.28~1/RELEASE_X86_64 x86_64
  • Subsystem: http2
  • Google Chrome: Version 83.0.4103.61 (Official Build) (64-bit)
  • Firefox: 76.0.1 (64-bit)

What steps will reproduce the bug?

Here is a simple echo server: return posted data to the client. This reproducing program runs locally.

const https = require("https");
const http2 = require("http2");
const fs = require("fs");

const serverKeyPath = "ssl_certs/server.key";
const serverCrtPath = "ssl_certs/server.crt";
const httpsPort = 8443;

const handler = (req, res) => {
  console.log(`${req.method} ${req.url}`);
  if (req.method === "GET") {
    res.writeHead(200, {
      'Content-Type': "text/html",
    });
    res.end(`\
<input type="file" id="myfile" style="font-size: 1.5em;">
<button id="post_btn" onclick="post()">Post</button><br>
<img id="myimage">
<script>
function post() {
  console.log(window.myfile.files);
  const xhr = new XMLHttpRequest();
  xhr.open("POST", "/post_test", true);
  const file = window.myfile.files[0];
  xhr.responseType = 'blob';
  xhr.onload = () => {
    window.myimage.src = URL.createObjectURL(xhr.response);
  };
  xhr.send(file);
}
</script>
  `);
    return;
  }

  // Echo-server
  req.pipe(res);

  req.on('close', () => {
    console.log(req.url, "closed");
  });
  req.on('end', () => {
    console.log(req.url, "ended");
  });
};

// You can switch
// const usesHttp2 = false;
const usesHttp2 = true;

const listenListener = () => {
  console.log(`Listen on ${httpsPort} with '${usesHttp2 ? "http2" : "https"}'...`);
};

if (usesHttp2) {
  http2.createSecureServer(
    {
      key: fs.readFileSync(serverKeyPath),
      cert: fs.readFileSync(serverCrtPath),
      // allowHTTP1: true,
    },
    handler,
  ).listen(httpsPort, listenListener);
} else {
  https.createServer(
    {
      key: fs.readFileSync(serverKeyPath),
      cert: fs.readFileSync(serverCrtPath),
    },
    handler,
  ).listen(httpsPort, listenListener); 
}

After run, you can access to https://localhost:8443/ on your browser.

You can create server.key and server.crt as follows.

openssl req \
  -newkey rsa:2048 \
  -x509 \
  -nodes \
  -keyout server.key \
  -new \
  -out server.crt \
  -subj /CN=localhost \
  -reqexts SAN \
  -extensions SAN \
  -config <(cat /etc/ssl/openssl.cnf \
      <(printf '[SAN]\nsubjectAltName=DNS:localhost,IP:192.168.0.1')) \
  -sha256 \
  -days 3650

How often does it reproduce? Is there a required condition?

Always.

What is the expected behavior?

Here is the expected behavior. When using const usesHttp2 = false;, we have the expected behavior with the "https" module.
node-https-post-success mp4 opt

What do you see instead?

With the "http2" module, we have the following when const usesHttp2 = true;.
node-http2-post-not-success mp4 opt

When the first POST, the server never returns the posted image and callbacks for on("close") and on("end") are not called. But, when you push the "post" button again, the first data are returned and the image is displayed.

In summary, a handler with "https" works well, but the handler with "http2" doesn't work well.

Additional information

This behavior is reproduced on Google Chrome and Firefox. The posted image is https://en.wikipedia.org/wiki/File:Lenna_(test_image).png#file.
Maybe related: #32978

@nwtgck nwtgck changed the title Posted body to a "http2" server are not "end" or "close" Request to HTTP/2 server is not handled on("end") or on("close"), and behave differently from HTTP/1 Jun 7, 2020
@nwtgck
Copy link
Author

nwtgck commented Aug 12, 2020

We still have this problem with Node.js (v12.18.3).

I found Firefox can upload but it's very delayed to display echoed image. Here is a demo on Firefox (79.0 (64-bit)).
Demo: https://youtu.be/ppWf687tIzU

@kanongil
Copy link
Contributor

I made an attempt at fixing this in #35209.

@nwtgck
Copy link
Author

nwtgck commented Sep 28, 2020

Thank you so much!

@targos targos added the http2 Issues or PRs related to the http2 subsystem. label Dec 27, 2020
@nwtgck
Copy link
Author

nwtgck commented Jul 14, 2021

This might be fixed by #33875.

I confirmed the code does not work in Node 14.8, but works in 14.9. The issue above mentioned like below.

It looks like this was released in v14.9.0 already per https://nodejs.org/en/blog/release/v14.9.0/.

#33875 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
http2 Issues or PRs related to the http2 subsystem.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants