-
Notifications
You must be signed in to change notification settings - Fork 184
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
Consolidation of HTTP codecs with gRPC #1198
Conversation
* API for <a href="https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#message-encoding"> message | ||
* coding schemes</a>. | ||
*/ | ||
public interface GrpcMessageEncoding { |
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 am on the fence about removing this. I guess we could keep this around and use this interface for gRPC (delegating to the HTTP codec), allowing for logic separation if we need it in the future. @idelpivnitskiy thoughts?
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.
Can you please clarify why you expect the need for gRPC-specific type?
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 can't think of something, that's why I removed it.
I am just considering whether keeping it allows for some flexibility on doing changes to one without affecting both.
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.
After thinking about it more, I'm agreed, it may be safer to have protocol-specific types that delegate the actual compression/decompression work to protocol-agnostic types.
We may have servicetalk-content-encoding-api
module that will have ContentCodec
class and identity, gzip, deflate implementations. Later we can create servicetalk-content-encoding-netty
for netty-based implementations.
Then we may have HttpContentEncoding
for http-api
and GrpcMessageEncoding
for grpc
modules. Each will delegate to the necessary API of ContentCodec
+ have an additional method to set up required header value:
For HttpContentEncoding
:
default HttpContentEncoding populateHeaders(final HttpHeader headers) {
headers.add(CONTENT_ENCODING, name());
headers.add(VARY, CONTENT_ENCODING);
return this;
}
For GrpcMessageEncoding
(should it extend HttpContentEncoding
?):
@Override
default GrpcMessageEncoding populateHeaders(final HttpHeader headers) {
headers.set(GRPC_MESSAGE_ENCODING_KEY, name());
return this;
}
WDYT?
servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/GrpcMessageEncodings.java
Outdated
Show resolved
Hide resolved
...rotobuf/src/main/java/io/servicetalk/grpc/protobuf/ProtoBufSerializationProviderBuilder.java
Outdated
Show resolved
Hide resolved
servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/GrpcMessageEncodings.java
Outdated
Show resolved
Hide resolved
* API for <a href="https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#message-encoding"> message | ||
* coding schemes</a>. | ||
*/ | ||
public interface GrpcMessageEncoding { |
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.
Can you please clarify why you expect the need for gRPC-specific type?
servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/GrpcUtils.java
Show resolved
Hide resolved
8a16823
to
39ec439
Compare
6701c4a
to
28b12dd
Compare
test this please |
28b12dd
to
c50b348
Compare
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.
Overall looks great!
Mostly minor comments:
|
||
@Override | ||
public long skip(long n) { | ||
int skipped = min(buffer.readableBytes(), (int) min(Integer.MAX_VALUE, n)); |
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.
buffer.readableBytes()
is int
. Can we simplify this to (int) min(buffer.readableBytes(), n)
?
We should also account for negative n
. The original skip
method from the superclass does:
if (n <= 0) {
return 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.
I added the negative guard, but the cast to integer might not be the right way.
Currently the max allowed input is INT_MAX
, if I relax that and cast the input, that could mean that an arbitrary input of (long 4294967297
-> int 1
) can be used to skip 1 bytes rather than all available bytes.
Unexpected behavior.
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.
You don't need to cast the input, min(buffer.readableBytes(), n)
will cast readableBytes
to long
. Because readableBytes
is int
, it can not be more than Integer.MAX_VALUE
. Then the long
result of the min
function can safely be cast back to int
: (int) min(buffer.readableBytes(), n)
.
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.
ofc 🤦
servicetalk-encoding-api/src/main/java/io/servicetalk/encoding/api/AbstractZipContentCodec.java
Show resolved
Hide resolved
servicetalk-encoding-api/src/main/java/io/servicetalk/encoding/api/AbstractZipContentCodec.java
Outdated
Show resolved
Hide resolved
* @param length the total length available for reading | ||
* @param allocator the {@link BufferAllocator} to use for allocating auxiliary buffers or the returned buffer | ||
* @return {@link Buffer} the result buffer with the content encoded | ||
*/ | ||
Buffer encode(Buffer src, int offset, int length, BufferAllocator allocator); | ||
Buffer encode(Buffer src, int length, BufferAllocator allocator); |
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.
Discussed offline and confirmed that we actually need the variant with length
for gRPC because the received Buffer
may contain more data that we want to encode/decode
. In this case, the offset, limit
is more standard API approach. Can you please reverth the offset
? Sorry for back and forth.
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 more standard API, but in our case it seems (to me) more confusing.
Buffer has the reader/writer indexes, which these methods mutate. So we typically mutate the readerIndex after consuming the buffer, to current_readerIndex + limit
. If we support offset
, that can be different than the readerIndex
, (before or after), what happens to the readerIndex if the offset + limit
< current_readerIndex
? Do we move it back to adhere to the contract or keep it unchanged?
I think since the Buffer
API allows easy positioning of the readerIndex, we should allow the caller to rely on that for offsetting.
WDYT?
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 point. Let's discuss offline what should be the default behavior of all codecs and deserializers. We are currently inconsistent. If we will decide to move indexes everywhere, then having only length
here is reasonable. If we decide not to move indexes, then offset
may be important.
servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/DefaultGrpcServiceContext.java
Outdated
Show resolved
Hide resolved
servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/GrpcUtils.java
Outdated
Show resolved
Hide resolved
servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/GrpcUtils.java
Outdated
Show resolved
Hide resolved
...rotobuf/src/main/java/io/servicetalk/grpc/protobuf/ProtoBufSerializationProviderBuilder.java
Outdated
Show resolved
Hide resolved
...rotobuf/src/main/java/io/servicetalk/grpc/protobuf/ProtoBufSerializationProviderBuilder.java
Outdated
Show resolved
Hide resolved
...rotobuf/src/main/java/io/servicetalk/grpc/protobuf/ProtoBufSerializationProviderBuilder.java
Outdated
Show resolved
Hide resolved
257b545
to
c49ceb4
Compare
c49ceb4
to
ad3c67e
Compare
test this please |
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.
A few minor comments and good to go. We can discuss the indexes move and offset
param for aggregated API in a follow-up.
@@ -1,5 +1,5 @@ | |||
/* | |||
* Copyright © 2018 Apple Inc. and the ServiceTalk project authors | |||
* Copyright © 2020 Apple Inc. and the ServiceTalk project authors |
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.
We should not override the year, we can append or change a region. Examples:
2018 -> 2018, 2020
2019 -> 2019-2020
2018-2019 -> 2018-2020
final Buffer dst = allocator.newBuffer(chunkSize); | ||
DeflaterOutputStream output = null; | ||
try { | ||
output = newDeflaterOutputStream(Buffer.asOutputStream(dst)); | ||
|
||
if (src.hasArray()) { | ||
output.write(src.array(), src.arrayOffset() + src.readerIndex(), length); | ||
src.readerIndex(src.readerIndex() + length); |
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 to make both paths consistent 🎖️
@@ -246,6 +262,7 @@ public void onNext(@Nullable final Buffer src) { | |||
// Not enough data to decompress, ask for more | |||
subscription.request(1); | |||
} catch (Exception e) { | |||
LOGGER.error("Error while decoding with " + name(), e); |
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.
Here and in all other places: consider using logger API to build a String
instead of concatenating manually:
LOGGER.error("Error while decoding with {}", name(), e);
|
||
@Override | ||
public long skip(long n) { | ||
int skipped = min(buffer.readableBytes(), (int) min(Integer.MAX_VALUE, n)); |
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.
You don't need to cast the input, min(buffer.readableBytes(), n)
will cast readableBytes
to long
. Because readableBytes
is int
, it can not be more than Integer.MAX_VALUE
. Then the long
result of the min
function can safely be cast back to int
: (int) min(buffer.readableBytes(), n)
.
@@ -67,6 +72,7 @@ default Buffer decode(Buffer src, BufferAllocator allocator) { | |||
|
|||
/** | |||
* Take a {@link Buffer} and decode its contents resulting in a {@link Buffer} with the decoded content. | |||
* This call increases the {@code readerIndex} of the {@code src} with the number of bytes read {@code length}. |
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.
Let's defer adding these details in javadoc?
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.
Since we concluded on moving indexes, I kept these changes too.
I think this is now consistent with other Buffer APIs.
* @param length the total length available for reading | ||
* @param allocator the {@link BufferAllocator} to use for allocating auxiliary buffers or the returned buffer | ||
* @return {@link Buffer} the result buffer with the content encoded | ||
*/ | ||
Buffer encode(Buffer src, int offset, int length, BufferAllocator allocator); | ||
Buffer encode(Buffer src, int length, BufferAllocator allocator); |
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 point. Let's discuss offline what should be the default behavior of all codecs and deserializers. We are currently inconsistent. If we will decide to move indexes everywhere, then having only length
here is reasonable. If we decide not to move indexes, then offset
may be important.
* List of supported {@link ContentCodec}s for this {@link GrpcSerializationProvider}. | ||
* Content codings will be used to encoded and decode gRPC messages according to configuration of client and server. | ||
* | ||
* @return list of supported {@link ContentCodec}s for this {@link GrpcSerializationProvider} |
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 is not fixed, reminder :)
Motivation:
Consolidate HTTP codec work with gRPC codecs for a unified API and implementation.
Modifications:
Re-use HTTP codecs for the non streaming version of HTTP.
Adhere to the same changes applied for HTTP, to allow for codec ordering.
Result:
Unified API & implementation between HTTP. & gRPC.