-
Notifications
You must be signed in to change notification settings - Fork 24.9k
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
Media-type parser #61987
Media-type parser #61987
Conversation
Pinging @elastic/es-core-infra (:Core/Infra/REST API) |
@elasticmachine run elasticsearch-ci/2 |
@elasticmachine run elasticsearch-ci/packaging-sample-windows |
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.
The SQL related changes look good, but I wonder if we can't simplify the change a bit.
XContentType acceptHeader = null; | ||
XContentType contentTypeHeader = null; |
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.
Wondering if this media-type origin split is strictly necessary: the previous code tried to fetch a token from somewhere - in order of preference: enforced format (CBOR), format
URL attribute, Accept
header, Content-Type
header and repeatedly checking accept
against null
- then validate that token against a media type or as a format (#fromMediaTypeOrFormat()
).
The new code checks if it's a format (in getXContentType()
@ L143), or then if it's a media type, by one of these XContentType instances (@ L147). But the source preferences is already stored in the way the accept
member is set (by those subsequent null
checks), so could we not simply serially invoke #fromMediaType(accept)
and #fromFormat(accept)
and return the first non-null value to simplify the code? Similar to what we do in TextFormat#fromMediaTypeOrFormat()
.
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, I have refactored this as per your suggestions
@@ -32,7 +34,7 @@ | |||
/** | |||
* Templating class for displaying SQL responses in text formats. | |||
*/ | |||
enum TextFormat { | |||
enum TextFormat implements MediaType { |
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.
Is the extension necessary?
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.
in order to use a MediaType parser for both TextFormat
and XContentType
i had to introduce an interface that is implemented by 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.
Right. Thanks.
@@ -92,7 +97,8 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli | |||
* that doesn't parse it'll throw an {@link IllegalArgumentException} | |||
* which we turn into a 400 error. | |||
*/ | |||
XContentType xContentType = accept == null ? XContentType.JSON : XContentType.fromMediaTypeOrFormat(accept); | |||
//TODO PG this all logic needs a review from SQL team |
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.
remove reminder.
@@ -197,6 +204,7 @@ String maybeEscape(String value, Character delimiter) { | |||
boolean hasHeader(RestRequest request) { | |||
String header = request.param(URL_PARAM_HEADER); | |||
if (header == null) { | |||
//TODO PG in most places we only assume one accept header |
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 to know. Having multiple header fields with same name is legal though, I guess we should leave it as is.
…rch into header_version_split
@@ -82,7 +82,8 @@ private XContentType detectContentType(String content) { | |||
//There must be a __<content_type__:: prefix so the minimum length before detecting '__::' is 3 | |||
int endOfContentName = content.indexOf("__::", 3); | |||
if (endOfContentName != -1) { | |||
return XContentType.fromMediaTypeOrFormat(content.substring(2, endOfContentName)); | |||
//TODO PG what do we expect here? |
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 can remove this todo, answered in another comment.
|
||
package org.elasticsearch.common.xcontent; | ||
|
||
public interface MediaType { |
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 add javadocs to the class and methods?
} | ||
|
||
/** | ||
* parsing media type that follows https://tools.ietf.org/html/rfc2616#section-3.7 |
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.
shouldn't we base this on https://tools.ietf.org/html/rfc7231#section-3.1.1.1
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 are right. the rfc2616 is obsolete.
rfc7231 is the updated one
Map<String, String> parameters = new HashMap<>(); | ||
for (int i = 1; i < split.length; i++) { | ||
String[] keyValueParam = split[i].trim().split("="); | ||
// should we validate that there are no spaces between key = value? |
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.
per the RFC, it is not allowed so I guess we should
x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/RestSqlQueryAction.java
Outdated
Show resolved
Hide resolved
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.
The SQL bits LGTM.
…ugin/RestSqlQueryAction.java Co-authored-by: Bogdan Pintea <[email protected]>
…rch into header_version_split
@elasticmachine run elasticsearch-ci/bwc |
@elasticmachine run elasticsearch-ci/default-distro |
is(nullValue())); | ||
|
||
assertThat(mediaTypeParser.parseMediaType(mediaType + "; key = value"), | ||
is(nullValue())); |
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 add a test for assertThat(mediaTypeParser.parseMediaType(mediaType + "; key=")
I think it will error with index out of bounds.
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 would mean that the result of String[] keyValueParam = split[i].trim().split("=");
has length = 1
it is checked in a next line.
if (keyValueParam.length != 2 || hasSpaces(keyValueParam[0]) || hasSpaces(keyValueParam[1])) {
added a testcase
|
||
/** | ||
* A media type object that contains all the information provided on a Content-Type or Accept header | ||
* // TODO PG to be extended with getCompatibleAPIVersion and more |
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 leave these specific TODO's out. IMO TODO's left in the code base should be rare and general enough such that any future dev can understand and implement it.
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.
agree. removed the todo
|
||
/** | ||
* Returns a corresponding format for a MediaType. i.e. json for application/json media type | ||
* Can differ from the MediaType's subtype i.e plain/text but format is txt |
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.
❤️
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.
LGTM, thanks for iterations. (a couple very minor new comments, with this PR or in the future)
@elasticmachine run elasticsearch-ci/1 |
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.
LGTM
libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaType.java
Outdated
Show resolved
Hide resolved
libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaTypeParser.java
Outdated
Show resolved
Hide resolved
Co-authored-by: Jay Modi <[email protected]>
This reverts commit 86ba732
Splitting method
XContentType.fromMediaTypeOrFormat
into two separate methods. This will help to validate media type provided inAccept
orContent-Type
headers.Extract parsing logic from
XContentType
(fromMediaType
andfromFormat
methods) to a separateMediaTypeParser
class. This will help reuse the same parsing logic forXContentType
andTextFormat
(used in sql)Media-Types type/subtype; parameters parsing is in defined https://tools.ietf.org/html/rfc7231#section-3.1.1.1
based on #61845
part of Allow parsing Content-Type and Accept headers with version #61427