Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 34.3k
Description
Version
v20.10.0
Platform
Linux temp-vps1 6.1.0-13-arm64 #1 SMP Debian 6.1.55-1 (2023-09-29) aarch64 GNU/Linux
Subsystem
http2
What steps will reproduce the bug?
Define a small http2 server that can send a stream, or receive a stream.
constfs=require("fs");conststream=require("stream");consthttp2=require("http2");constcrypto=require("crypto");constserver=http2.createSecureServer({key: fs.readFileSync("./localhost-privkey.pem"),cert: fs.readFileSync("./localhost-cert.pem"),allowHTTP1: true,// http2 upgradesettings: {initialWindowSize: 8*1024*1024}});server.on('error',(err)=>console.log(err));constbuffer=crypto.randomBytes(256*1024*1024);server.on("connect",(session)=>{session.setLocalWindowSize(8*1024*1024);});server.on("stream",(st,headers)=>{st.respond({'content-type': 'application/octet-stream',":status": 200,});if(headers[':path']==="/download"){stream.Readable.from(buffer).pipe(st);}else{st.on('data',d=>st.resume());st.on('error',console.error);st.on('end',()=>st.close());}})server.listen(8443,()=>{console.log("HTTPS (HTTP/2) listening on 8443 port");});generate ssl certs:
openssl req -x509 -newkey rsa:2048 -nodes -sha256 \ -subj '//CN=localhost' -keyout localhost-privkey.pem -out localhost-cert.pem go to a different machine (preferably on the same local network) and run:
curl --http2 -k https://other-machine:8443/download --output /dev/null dd if=/dev/urandom of=test.bin bs=100M count=4 curl --http2 -k -T test.bin https://chunky.swat.other-machine:8443/upload observer the difference in speed between up and downstream.
$ curl --http2 -k https://other-machine:8443/download --output /dev/null % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 256M 0 256M 0 0 356M 0 --:--:-- --:--:-- --:--:-- 356M $ curl --http2 -k -T test.bin https://other-machine:8443/upload % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 400M 0 0 100 400M 0 11.7M 0:00:33 0:00:33 --:--:-- 11.7M How often does it reproduce? Is there a required condition?
I've manged to reproduce this with multiple vps on different cloud providers. switching around server and client side.
Loopback never reproduces it, and sometimes if you have 2 machines on the same switch, it also doesn't reproduce.
I suspect it's some kind of window related issue, I've already applied the manual fixes suggested in #38426. Without these the download speed is also this slow.
What is the expected behavior? Why is that the expected behavior?
The performance should be comparable to raw tcp speed (with some lee-way for http2 overhead).
See netcat performance between the 2 machines:
on server:
nc -l -p 5050 > /dev/null on second machine:
pv -a test.bin| nc other-machine 5050 [ 492MiB/s] using nodejs as a http2 client has same performance issue
constfs=require("fs");consthttp2=require("http2");constprocess=require("process");constcrypto=require("crypto");conststream=require("stream");constbuffer=crypto.randomBytes(256*1024*1024);letround=0;functionupload(){constclient=http2.connect(process.env["TARGET_HOST"]??"https://localhost:8443",{ca: fs.readFileSync("./localhost-cert.pem"),checkServerIdentity: ()=>undefined,settings: {initialWindowSize: 8*1024*1024}});client.once('remoteSettings',settings=>{client.setLocalWindowSize(settings.initialWindowSize??(8*1024*1024));});constrecv=client.request({":path": "/upload"},{endStream: false});recv.on("response",()=>{conststart=process.hrtime();stream.Readable.from(buffer).pipe(recv).on("end",()=>{const[timeSpendS,timeSpendNS]=process.hrtime(start);constmbSize=buffer.length/(1024*1024);console.log(`Uploading to h2 speed (${Math.trunc(mbSize)} MB): ${mbSize/(timeSpendS+timeSpendNS*1e-9)} MB/s`);client.close();round++;if(round<3){// to give node a fair chance of getting close to curl// we go throught the upload 3 times (sequentually).// such that node has had time to JIT etc.upload();}});});}upload()invoke with: TARGET_HOST="https://other-machine:8443" node n2-upload.js
What do you see instead?
Slow upload while the tcp connnection speed would allow for fast upload.
Additional information
I'm closing a previous issue (#50972) I reported, that one got too complex and big. This is a much smaller case and easier to debug, I hope. If there is something I can do to help out, please let me know.
I think this is not a dupe of #38426 as even when you manually setup a big enough window, the receive side is still slow. I suspect there is a call to nghttp2 missing that is not exposed on the nodejs side?