Description
What version of gRPC-Java are you using?
1.41.x, but also observed in master branch
What is your environment?
Linux/MacOs, n/a
What did you expect to see?
Metadata validation is consistent between Java and Go implementations.
What did you see instead?
Java treats differently some invalid characters.
These are the rules for header values:
- ASCII-Value should not have leading or trailing whitespace.
- ASCII-Value → 1*( %x20-%x7E ) ; space and printable ASCII
- "Implementations must not error due to receiving an invalid ASCII-Value that's a valid field-value in HTTP, but the precise behaviour is not strictly defined: they may throw the value away or accept the value. If accepted, care must be taken to make sure that the application is permitted to echo the value back as metadata."
About rule 1 - ASCII-Value should not have leading or trailing whitespace.
We have not yet tested all the cases but it seems like neither Java nor Go are validating for this. Doesn't seem like there is a strong need to implement this.
About rule 2 - ASCII-Value → 1*( %x20-%x7E ) ; space and printable ASCII
The current metadata object in grpc-java relies on encoded.getBytes(US_ASCII);
:
grpc-java/api/src/main/java/io/grpc/Metadata.java
Lines 981 to 985 in 5081e60
grpc-java/api/src/main/java/io/grpc/Metadata.java
Lines 342 to 353 in 5081e60
The effects of this are that depending on the type of invalid character, the headers have different behaviours:
- Characters outside of US_ASCII: The invalid character gets replaced by the replacement character (�), and after that substituted again for a question mark (?).
- Non printable characters inside of US_ASCII range (e.g: \t or \n) are not substituted. This leads to the header being removed from the request.
In grpc-go there is a straight forward validation of all the bytes before sending the request. If the validation fails the request call results in error.
About rule 3 - If accepted, care must be taken to make sure that the application is permitted to echo the value back as metadata.
Java uses the same validation as for request headers:
- Characters outside of US_ASCII: The application code in the server will see the received header with the character substituted by the replacement character (�). When echoing back the header, the server will send the header with the character substituted by the question mark (?)
- Non printable characters: The application code in the server will see those characters in the received headers but will drop the header from the response.
Go doesn't validate anything in the received headers nor in the response headers, so you are able to send back any header. You are also able to send any new headers that break all the rules in the response ( eg: "invalid-$":"Ĩñvá \t łið" )
Steps to reproduce the bug
Edit: Fixed misunderstanding from our side.