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

HttpOutgoingMessage.writable is always true #42610

Open
clshortfuse opened this issue Apr 4, 2022 · 3 comments
Open

HttpOutgoingMessage.writable is always true #42610

clshortfuse opened this issue Apr 4, 2022 · 3 comments
Labels
http stream

Comments

@clshortfuse
Copy link

@clshortfuse clshortfuse commented Apr 4, 2022

Version

v14.19.1 / v16.14.2

Platform

Linux ROGLaptop 5.10.102.1-microsoft-standard-WSL2 #1 SMP Wed Mar 2 00:30:59 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

Subsystem

http

What steps will reproduce the bug?

const { ServerResponse } = require('http');

const res = new ServerResponse({});

console.log(res.writable); // true

res.destroy(); 

console.log(res.writable); // true

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

Always

What is the expected behavior?

.writable should be false on destroy

What do you see instead?

.writable is always true

Additional information

Since with v14+, streams no longer output errors when destroyed/ended, you need to check before piping to them. On any other stream (eg: Http2, Transform, Passthrough, Writable), you can check with .writable. This doesn't work on Http1 ServerResponse objects.

Normally, this code point is used to check if writable:

return !!w && w.writable !== false && !w.destroyed && !w.errored &&
!w.ending && !w.ended;

Instead, it's always true:

this.writable = true;

It would require special steps to see if we are piping / writing to an Http1 ServerResponse and then handle differently, or just abandoning .writable eg:

return (stream instanceof ServerResponse ? (!stream.destroyed && !stream.writableEnded) : stream.writable)

I'm not sure how to safely check again errored, but I'd assume it would always signal destroyed.

@targos
Copy link

@targos targos commented Apr 5, 2022

@targos targos added http stream labels Apr 5, 2022
@ronag
Copy link

@ronag ronag commented Apr 5, 2022

Unfortunately ServerResponse is not a real stream and not entirely consistent. We've tried to fix this, but unfortunately it breaks the ecosystem. Maybe we can do another take on it @mcollina?

streams no longer output errors when destroyed/ended, you need to check before piping to them

Not sure what you mean here? You don't need to check anything if you us pipeline.

'm not sure how to safely check again errored, but I'd assume it would always signal destroyed.

Your assumption should be correct.

@clshortfuse
Copy link
Author

@clshortfuse clshortfuse commented Apr 5, 2022

Not sure what you mean here? You don't need to check anything if you us pipeline.

Not needed for pipeline. It is needed for .pipe because unwritable streams can't emit 'end' or 'error'. I used to do something like this (simplified), because I was sometimes streaming chunks from a CDN, or piping it.

if (!destination.writable) return false; // Guard against `.pipe` leak
try { 
  if (source instanceof Readable) {
    destination.on('close', ...);
    destination.on('end', ...);
    source.pipe(destination);
  } else {
    destination.write(source);
  }
} catch ( e ) { return false };
return true;

If destination is ServerResponse (http1), source would never be consumed and never be released and garbage collected, causing a leak. Small because I mostly have HTTPS/HTTP2 traffic, but it happens. The try/catch probably isn't needed after v14 since ERR_STREAM_DESTROYED no longer throws on write. Not 100% sure. But, I have to replace the first line, I believe, with this:

  function isWritable(writableLike) {
    return (!writableLike.destroyed && !writableLike.writableEnded);
  }

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

No branches or pull requests

3 participants