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

Static table only QPACK #3

Merged
merged 15 commits into from
Nov 30, 2020
Merged

Static table only QPACK #3

merged 15 commits into from
Nov 30, 2020

Conversation

kachayev
Copy link
Contributor

@kachayev kachayev commented Nov 28, 2020

Motivation:

QPACK is headers encoding algorithms essential for H3 protocol.

Modifications:

  • Key additions are QpackEncoder, QpackDecoder and QpackStaticTable, API is shown in the test case.
  • I made a small Python script to parse static table entries from Appendix A of the spec and generate Java code, didn't include the script itself though.
  • Logic of name/value and name-only indexing is implemented the same way static table works for HPACK with only caveats for 0-based indexing used in QPACK. I'm not convinced this is the faster approach though, I want to play around with different structures & search methods to test out performance. Also, encoder calls static table twice to get name/value or name-only matches. It seems like this could be optimized by returning masked index to differentiate 2 cases.
  • Http3Headers, DefaultHttp3Headers are copy-pasted from HTTP/2 codec (corresponding classes). Not sure if we even need H3 headers to be defined separately or cleanup HTTP/2 implementation to have less dependencies on itself, e.g. HTTP/2-specific exceptions.
  • HuffmanEncoder and HuffmanDecoder are copy-pastes as well. Those classes are package-private in HTTP/2 code and decoder uses HTTP/2-related exception. It would be nice to move both into utils or common instead of keep 2 separate copies. Same goes for CharSequenceMap.
  • Http3Exception is some sort of a placeholder for future errors processing layer. Not sure if we want to follow HTTP/2 codec model though.
  • Headers validation for QpackDecoder is not implemented yet. Once again, it would be nice if we can cleanup HTTP/2 implementation to be able to re-use blocks of it (including headers sink and header validation logic).
  • Neither encoder nor decoder support dynamic table control messages (I will add it later when working on a dynamic table support).
  • Literal names and values are always huffman compressed which might be not optimal in all cases. I will look into making the same optimization HPACK is doing when deciding between sending raw or compressed value.
  • HPACK decoder implemented using state transition flow (I assume to reduce function calls overhead, right?). I've implemented decoder using functions as it was much easier to keep track of the logic. I would rather prefer to make sure that the implementation is, in fact, correct before working on optimizations. Plus, having good benchmarks is also must have before optimizing.

Copy link
Member

@normanmaurer normanmaurer left a comment

Choose a reason for hiding this comment

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

First quic review

public void validateName(CharSequence name) {
if (name == null || name.length() == 0) {
PlatformDependent.throwException(
new Http3Exception(String.format("empty headers are not allowed [%s]", name)));
Copy link
Member

Choose a reason for hiding this comment

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

let's update the validateName methods signature to include throws declaration so we don't need to use this hack

Copy link
Contributor Author

@kachayev kachayev Nov 29, 2020

Choose a reason for hiding this comment

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

Errors/exceptions handling definitely needs some love here. So far, it's mostly copy from HTTP/2

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This one turned out to be a pretty deep hole: it relies on an interface from codec. I'm going to just leave it "as is" for now. We can change it later

@kachayev
Copy link
Contributor Author

kachayev commented Nov 29, 2020

@normanmaurer I pushed quite a significant change on how static table lookup works. See QpackStaticTable::findFieldIndex function. Rather than resolving name -> index twice (as http2 codec HPACK is doing), the function "memorizes" name index and return it when exact match is not found. Masked in a way that caller could differentiate field index from name index.

@normanmaurer
Copy link
Member

@kachayev looks great... let me know when you think its ready for a merge. We can do a followup later when we want to improve it / find bugs.

@kachayev
Copy link
Contributor Author

@normanmaurer I will do another pass tomorrow, mainly to cleanup errors handling and validation. And will open issues for enhancements that I've mentioned in the very beginning. Also, I'm going to make a PR for codec-http2 with the same trick to avoid double hash map lookup.

@normanmaurer
Copy link
Member

Sounds good 👌

@normanmaurer
Copy link
Member

@kachayev wdyt about moving the Huffman stuff + CharSequenceMap into the codec package (just like the other compression stuff) so we could reuse it from h2 and h3.

@kachayev
Copy link
Contributor Author

kachayev commented Nov 29, 2020

@normanmaurer Totally agree. I've mentioned this earlier

HuffmanEncoder and HuffmanDecoder are copy-pastes as well. Those classes are package-private in HTTP/2 code and decoder uses HTTP/2-related exception. It would be nice to move both into utils or common instead of keep 2 separate copies. Same goes for CharSequenceMap.

Decoder needs to be cleanup a bit as it throws HTTP/2 connection error, - this logic should stay in codec-http2. I can work on PR

@kachayev
Copy link
Contributor Author

@normanmaurer I think it's good so far... Errors handling (e.g. validation exceptions you've mentioned) and general logic of Http3Exception would be much easier to work on when the rest of the code is shaped. E.g. some decoder problems should shutdown the stream, some the entire connection. I think we should first identify all use cases and then adjust implementation where necessary


private static final long DEFAULT_MAX_HEADER_LIST_SIZE = 0xffffffffL;

private final QpackHuffmanDecoder huffmanDecoder = new QpackHuffmanDecoder();
Copy link
Member

Choose a reason for hiding this comment

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

Just to clarify... This could be just the "standard" HuffmanDecoder that would be shared with http2 in the future, correct @kachayev ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, exactly. The only thing we will need to change:

private static final Http3Exception BAD_ENCODING = new Http3Exception("QPACK - Bad Encoding");

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When moving to codec package, this exception should be changed to something fairly generic. Meaning that the code should wrap decoding into try/catch to produce meaningful error information "locally"

@normanmaurer normanmaurer merged commit 8110ecc into netty:main Nov 30, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants