-
Notifications
You must be signed in to change notification settings - Fork 524
Conversation
…e BufferReader tests (will copy the new ones here later). Adding the rest of the changes in the next update.
Added the core of the BufferReader struct changes. Working on the actual meat of the parser changes now. |
…er expect the trailing CR/LF.
I've changed the request line parser. As part of this change I'm changing tests to no longer expect carriage returns and line feeds in error messages. I presume this is ok? |
…where we change things in situ.
Okay, the header parsing is there too. Again, this change makes the detail reported on errors end before any CR/LF. It seems like that is the right answer, it would be difficult/costly to pass the context along to include up to the first LF as before. The next step is to call straight into the methods that take the reader. That has a significant impact when parsing the entire request. |
Fyi, I'm using this as a sanity check for getting |
Ok, I've updated the benchmarks to use the overloads that take the reader and done the initial microbenchmarks. Before
After
Looking promising. 😄 |
Nice results @JeremyKuhne ! |
/cc @stephentoub @danmosemsft |
{ | ||
RejectRequestHeader(headerLine, length); | ||
RejectRequestHeader(headerLine); |
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 think this should change to a message that states that it found an unexpected CR or LF. Then it would be super clear that the detail message is followed by such. My change cuts the span before CR or LF, the previous code cut before any LF. I'd want to do that in a separate change though I think. Thoughts? @pakrym?
…max parse when you want to keep the state going with the reader. Clean up lack of var usage.
@davidfowl, @pakrym I think this is now in a finished state. I've run all of the Before
After
Before
After
Before
After
The rest of the Current state and next stepsThis change removes essentially all of the unsafe code in HttpParser and makes it significantly faster. It relies on the It also gets performance gains by changing the way we parse. It breaks up valid lines by CRLF and only passes the "good" line for further parsing, which simplifies the inner parsing logic. When iterating through the design in CoreFXlab I found that to be the source of about half of the performance gains. The |
Note that @GrabYourPitchforks Span/Memory perf changes should hopefully improve things even further. |
Nice 😄 |
} | ||
|
||
buffer = buffer.Slice(consumed); |
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.
Removing the two slices of the ref buffer param in this method always results in an infinite loop in BenchmarkApplication.ProcessRequestsAsync() since buffer.IsEmpty never becomes true.
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.
Good catch- fixing.
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.
Add test to catch this? (e.g. count iterations)
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.
Add test to catch this? (e.g. count iterations)
Not sure where that would go.
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.
Not sure where that would go.
Something based on ProcessRequestsAsync ensuring its not stuck in an infinite loop?
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 don't see that we have a reference to it anywhere from tests. I'm a little hesitant to start adding them given my lack of experience in this codebase...
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.
59e1ff7 probably works too :)
The benchmark apps will have to change anyway to match the interface change (e.g. https://github.com/TechEmpower/FrameworkBenchmarks/blob/master/frameworks/CSharp/aspnetcore/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs#L96)
new[] { "Header-1: value1\r\nHeader-2: value2\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-2: value2\x0A") }, | ||
new[] { "Header: value\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header: value") }, | ||
new[] { "Header-1: value1\nHeader-2: value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-1: value1") }, | ||
new[] { "Header-1: value1\r\nHeader-2: value2\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-2: value2") }, |
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 too bad that the invalid header error messages no longer really show what's wrong when there's an issue with the CRLF sequences. These errors are rare enough that I'm OK with it if it's too difficult to retain the old behavior, but I like the old behavior better.
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 think we should change the error message whenever we find a bad CR/LF and then it would be pretty easy to understand. Something like Unexpected CR or LF in header: '{detail}'
.
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.
Could even put a separate message up for the two different cases:
Unexpected CR in header: '{detail}'
or possibly Unexpected CR without LF in header: '{detail}'
(although this sort of infers that the header is fine other than that, which isn't necessarily true).
Now that I'm looking at it, I could also rewind the reader and take the bad CR or LF into the detail message... Hmm. Maybe that is the right place to start...
Unexpected LF in header: '{detail}'
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 like distinct error messages. Even just Unexpected (CR|LF) in header: '{header up until the CR or LF}'
any time you see one without the other is enough for me.
public bool TakeMessageHeaders(ref BufferReader<byte> reader, long length) | ||
{ | ||
var consumed = reader.Consumed; | ||
var remaining = length - consumed; |
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.
This annoying extra bookkeeping to keep track of the underlying ReadOnlySequence's length makes me think that BufferReader should offer a cached, lazily-initialized Length property. An efficient Remaining property could also be built on top of this.
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 is definitely worth considering. I'm being very cautious about growing the size of the reader, but at first blush it seems useful enough to take the extra field.
@JeremyKuhne is this good to go? @sebastienros We want to run an end to end perf test here, where is the bottt 😄 |
I've run plaintext on this:
Platform benchmark is unfortunately broken in master. |
@davidfowl Yes, I think I'm done here. |
@dotnet-bot test Ubuntu 16.04 Debug Build |
@davidfowl, @pakrym Is there anything else you want done here or can this be merged? Also, how should we track flipping to |
We can't merge this until we have a 3.0 baseline (which I'm hoping will be soon). |
@davidfowl did you see @pakrym ran these already ? |
@sebastienros that's not good enough. I'd like us to do regular 3.0 runs against master so that we're not surprised anymore. Once we close down 2.2 and switch to 3.0 then we can take this change and see if it affects that baseline. I'm going to be super anal about changes we can't detect now 😄 |
Kestrel work has moved to aspnetcore repo, and this repo will be archived. If you're still interested in pursuing this PR, please move it over to aspnetcore. Thanks. |
@pakrym, @davidfowl This is the simple "convert to span" change. I'm porting the changes I made in CoreFxLab on top of this.