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

Bug Fix - MiniSSL::Socket#write - use data.byteslice(wrote..-1) #2543

Merged
merged 1 commit into from Feb 1, 2021

Conversation

MSP-Greg
Copy link
Member

@MSP-Greg MSP-Greg commented Jan 30, 2021

Description

MiniSSL::Socket#write performs some string 'dice & chop', and was using both byte and character methods. Convert all to byte methods.

Closes #2542

Note that this is only noticeable when the response headers/body are such that x.length != x.bytesize

I will write a test for it, but did the checking locally with a 600kB body. Was using curl and git diff, need to find a more Ruby based solution...

Your checklist for this pull request

  • I have reviewed the guidelines for contributing to this repository.
  • I have added an entry to History.md if this PR fixes a bug or adds a feature. If it doesn't need an entry to HISTORY.md, I have added [changelog skip] or [ci skip] to the pull request title.
  • I have added appropriate tests if this PR fixes a bug or adds a feature.
  • My pull request is 100 lines added/removed or less so that it can be easily reviewed.
  • If this PR doesn't need tests (docs change), I added [ci skip] to the title of the PR.
  • If this closes any issues, I have added "Closes #issue" to the PR description or my commit messages.
  • I have updated the documentation accordingly.
  • All new and existing tests passed, including Rubocop.

@@ -133,7 +133,7 @@ def write(data)

return data_size if need == 0

data = data[wrote..-1]
data = data.byteslice(wrote..-1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MSP-Greg Do you understand why 5.1.1 doesn't seem to have the problem stated in #2542? This line of code wasn't changed in #2519 (but surrounding code was: https://github.com/puma/puma/pull/2519/files#diff-5f9e58f45420759185f3cff5203a523482b915cc3e331bb0f060977c2078d903)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will look at 5.1.1 and 5.2.0. I believe the bug requires a response with multi-byte characters, longer than 16kB, and ssl. And, since some html parsers can be rather forgiving...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm reading the code right, I think the bug was in changing line 133 from data.bytesize to data, no?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confused. Do you mean changing data.bytesize to data_size, which is set to the value of data.bytesize?

Anyway, I'm working thru it. The code in 5.1.1 was writing 512 byte chunks to the TCPSocket, which seemed not optimized. But, it also appears that it wrote all the data with @engine.write, where the current code is writing it in 16kB chunks. Because 5.1.1 was writing everything with @engine.write, maybe the issues with how it was looping weren't apparent?

Trying to figure out why the difference in write size is happening... Regardless, I believe the current code is a lot faster?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to review the code changes in minissl.c, but I now recall. It's involved, but I'll write it up.

@nateberkopec nateberkopec added bug ssl waiting-for-review Waiting on review from anyone labels Jan 30, 2021
@MSP-Greg
Copy link
Member Author

First, the question of why did MiniSSL::Socket#write work in 5.1.1. Note that the code has two while loops. With the SSLContext options used in 5.1.1, the outer loop never looped, as all of ‘data’ was written in @engine.write data. Using the 600kB body, the code wrote the whole string with @engine.write, extracted it in 512 byte chunks, and wrote each to the TCPSocket.

Secondly, regarding the recent changes in Puma’s SSL for 5.2.0, I decided something was wrong while working on the new test framework, as it logged connection times. There was quite a large difference between TCP/Unix sockets and SSL sockets.

Also, I was aware of 'Puma 4.3.6 - Rails 6.0.3.4 - TLS brings in memory leak'. That wasn’t ever really pinned down, but I thought it possible that a large number of open/idle ssl clients could use a fair bit of memory.

Hence, the changes in 5.2.0. I changed the code to share an SSLContext, instead of creating a new one for each accepted connection, made some other adjustments to the Context, and re-wrote MiniSSL::Socket.write. Now, with the 600kB body, it’s written in 16kB chunks, extracted in 4kB chunks, which are assembled into a 16kb string, which is then written to the TCPSocket.

I thought this would be a reasonable implementation to both improve the speed and lower the memory. Testing locally, times have dropped for responses with both 10kB and 200kB bodies. The options chosen should reduce the memory required for open connections (keep-alive). I haven’t referenced OpenSSL man pages, but I (re-)read quite a few for the update…

@nateberkopec
Copy link
Member

the outer loop never looped

OK, now I get it

@nateberkopec nateberkopec merged commit a5c4b1e into puma:master Feb 1, 2021
@MSP-Greg
Copy link
Member Author

MSP-Greg commented Feb 1, 2021

OK, now I get it

Thanks for looking at it. I hope the explanation was reasonable, it's kind of messy...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug ssl waiting-for-review Waiting on review from anyone
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Puma::MiniSSL::SSLError: System error: Undefined error: 0 - 0 with 5.2.0
3 participants