-
Notifications
You must be signed in to change notification settings - Fork 160
feat: implement https server support #602
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
base: master
Are you sure you want to change the base?
Conversation
- Fix a datarace for error handler - Add a regression test that verify datarace fix - Add TLS defaults for better security
Could you please point me to the changes that are related to the fixes? Thanks. Also, what was wrong with the statistics? |
Don't remember right now for 100%, but few tests failed for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, had a few comments.
In addition to that, could you please:
- Bump the package version
- Describe all the new things in
README.md(which serves as the primary user-facing documentation)
Thanks
Aslo revert changes in eslint and tsconfig
|
@jirimoravcik @lewis-wow Guys, I've added main logic for TLS overhead bytes. Please take a look 🙏 Gonna polish tests in meantime and push 'em ASAP. |
|
|
||
| // Check once per connection for socket._parent availability. | ||
| if (this.serverType === 'https') { | ||
| const rawSocket = socket._parent; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be worth asking in https://github.com/nodejs/node about the safety of using this private property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I know the answer :) as long as unit tests cover the eventuality this will get removed, I think we're good
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It’s not just about the presence of the _parent property, but about the overall usability for stats tracking. I mean, if you consider yourself an expert on the TLS implementation in Node.js, that’s great. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fyi: nodejs/help#5111 🤞
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Have nothing to add.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found a few more points for discussion
| if (options.serverType === 'https') { | ||
| if (!options.httpsOptions) { | ||
| throw new Error('httpsOptions is required when serverType is "https"'); | ||
| } | ||
|
|
||
| // Apply secure TLS defaults (user options can override) | ||
| // This prevents users from accidentally configuring insecure TLS settings | ||
| const secureDefaults: https.ServerOptions = { | ||
| ...HTTPS_DEFAULTS, | ||
| honorCipherOrder: true, // Server chooses cipher (prevents downgrade attacks) | ||
| ...options.httpsOptions, // User options override defaults | ||
| }; | ||
|
|
||
| this.server = https.createServer(secureDefaults); | ||
| this.serverType = 'https'; | ||
| } else { | ||
| this.server = http.createServer(); | ||
| this.serverType = 'http'; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe validate if options.serverType is one of http, https? It would make it consistent with the type. I'd also set it to http by default in the constructor parameter. That way you could just do this.serverType = options.serverType
| socket.proxyChainErrorHandled = true; | ||
|
|
||
| // Log errors only if there are no user-provided error handlers | ||
| if (this.listenerCount('error') === 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was === 1 before, why is it === 0 now?
| // Socket contains application bytes only. | ||
| let srcTxBytes = socket.bytesWritten ?? 0; | ||
| let srcRxBytes = socket.bytesRead ?? 0; | ||
|
|
||
| if (this.serverType === 'https' && socket.tlsOverheadAvailable) { | ||
| /* eslint no-underscore-dangle: ["error", { "allow": ["_parent"] }] */ | ||
| // Access underlying raw socket to get total bytes (app + TLS overhead). | ||
| const rawSocket = socket._parent; | ||
| if (rawSocket && typeof rawSocket.bytesWritten === 'number' && typeof rawSocket.bytesRead === 'number') { | ||
| if (rawSocket.bytesWritten >= socket.bytesWritten && rawSocket.bytesRead >= socket.bytesRead) { | ||
| srcTxBytes = rawSocket.bytesWritten; | ||
| srcRxBytes = rawSocket.bytesRead; | ||
| } else { | ||
| // This should never happen, log for debugging. | ||
| this.log(connectionId, `Warning: TLS overhead count error.`); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| const targetStats = getTargetStats(socket); | ||
|
|
||
| const result = { | ||
| srcTxBytes: socket.bytesWritten, | ||
| srcRxBytes: socket.bytesRead, | ||
| trgTxBytes: targetStats.bytesWritten, | ||
| trgRxBytes: targetStats.bytesRead, | ||
| return { | ||
| srcTxBytes, // HTTP: app only, HTTPS: total (app + TLS overhead) | ||
| srcRxBytes, // HTTP: app only, HTTPS: total (app + TLS overhead) | ||
| trgTxBytes: targetStats?.bytesWritten, | ||
| trgRxBytes: targetStats?.bytesRead, | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just an idea. Why don't we store the _parent socket in this.connections? Looking at the logic here in getConnectionsStats, you just fall back to the original socket for stats. That makes me question why would we ever want to use the TLS socket for connection tracking?
That gives me another idea, if you do this.server.on('connection') for HTTPS you should be able to reach the original Socket without using _parent, right?
This PR implements HTTPS server support to proxy-chain.
Also fixed:
errorevent when we might log same eventsOtherwise my changes should be fully compatible with HTTP server and all the handlers.
Readiness checklist:
_parent, include overhead bytes intosrcXxBytes