-
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
Allow parsing Content-Type and Accept headers with version #61427
Changes from 1 commit
7514d87
921bc6f
ee280ba
433de74
51467e0
ccaadbd
de13d4b
d9ee420
41150a6
2fc8f86
734031a
c04af65
474eb29
5c30813
7b760ba
5853561
b6ebd63
ff6c94f
b969f2b
0684e58
b91d472
9edc49f
c5d8166
ad673b6
1ae6a60
1ae63c6
ee1e5a0
9f4b6c3
bd4dee1
ce42f1b
0f8ae7b
c4f02ad
176302e
eab471c
e128fd7
66435dd
08f1395
3f3f0d5
427c391
e031605
4134d92
a976f5b
3e0eec0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,8 @@ | |
|
||
import java.util.Locale; | ||
import java.util.Objects; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
/** | ||
* The content type of {@link org.elasticsearch.common.xcontent.XContent}. | ||
|
@@ -114,13 +116,19 @@ public XContent xContent() { | |
} | ||
}; | ||
|
||
private static final Pattern COMPATIBLE_API_HEADER_PATTERN = Pattern.compile( | ||
"(application|text)/(vnd.elasticsearch\\+)?([^;]+)(\\s*;\\s*compatible-with=(\\d+))?", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Am I reading this correctly that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you are right. This is possibly a bit oversimplified. The more strict could look like
there is a simplistic validation in |
||
Pattern.CASE_INSENSITIVE); | ||
|
||
/** | ||
* Accepts either a format string, which is equivalent to {@link XContentType#shortName()} or a media type that optionally has | ||
* parameters and attempts to match the value to an {@link XContentType}. The comparisons are done in lower case format and this method | ||
* also supports a wildcard accept for {@code application/*}. This method can be used to parse the {@code Accept} HTTP header or a | ||
* format query string parameter. This method will return {@code null} if no match is found | ||
*/ | ||
public static XContentType fromMediaTypeOrFormat(String mediaType) { | ||
public static XContentType fromMediaTypeOrFormat(String mediaTypeHeaderValue) { | ||
String mediaType = parseMediaType(mediaTypeHeaderValue); | ||
|
||
if (mediaType == null) { | ||
return null; | ||
} | ||
|
@@ -130,7 +138,7 @@ public static XContentType fromMediaTypeOrFormat(String mediaType) { | |
} | ||
} | ||
final String lowercaseMediaType = mediaType.toLowerCase(Locale.ROOT); | ||
if (lowercaseMediaType.startsWith("application/*")) { | ||
if (lowercaseMediaType.startsWith("application/*") || lowercaseMediaType.equals("*/*")) { | ||
jaymode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return JSON; | ||
} | ||
|
||
|
@@ -142,7 +150,9 @@ public static XContentType fromMediaTypeOrFormat(String mediaType) { | |
* The provided media type should not include any parameters. This method is suitable for parsing part of the {@code Content-Type} | ||
* HTTP header. This method will return {@code null} if no match is found | ||
*/ | ||
public static XContentType fromMediaType(String mediaType) { | ||
public static XContentType fromMediaType(String mediaTypeHeaderValue) { | ||
String mediaType = parseMediaType(mediaTypeHeaderValue); | ||
|
||
final String lowercaseMediaType = Objects.requireNonNull(mediaType, "mediaType cannot be null").toLowerCase(Locale.ROOT); | ||
for (XContentType type : values()) { | ||
if (type.mediaTypeWithoutParameters().equals(lowercaseMediaType)) { | ||
|
@@ -157,6 +167,28 @@ public static XContentType fromMediaType(String mediaType) { | |
return null; | ||
} | ||
|
||
//public scope needed for text formats hack | ||
jaymode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public static String parseMediaType(String mediaType) { | ||
if (mediaType != null) { | ||
Matcher matcher = COMPATIBLE_API_HEADER_PATTERN.matcher(mediaType); | ||
if (matcher.find()) { | ||
return (matcher.group(1) + "/" + matcher.group(3)).toLowerCase(Locale.ROOT); | ||
} | ||
} | ||
|
||
return mediaType; | ||
} | ||
|
||
public static String parseVersion(String mediaType){ | ||
if(mediaType != null){ | ||
Matcher matcher = COMPATIBLE_API_HEADER_PATTERN.matcher(mediaType); | ||
if (matcher.find() && "vnd.elasticsearch+".equalsIgnoreCase(matcher.group(2))) { | ||
|
||
return matcher.group(5); | ||
} | ||
} | ||
return null; | ||
} | ||
private static boolean isSameMediaTypeOrFormatAs(String stringType, XContentType type) { | ||
return type.mediaTypeWithoutParameters().equalsIgnoreCase(stringType) || | ||
stringType.toLowerCase(Locale.ROOT).startsWith(type.mediaTypeWithoutParameters().toLowerCase(Locale.ROOT) + ";") || | ||
|
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.
Regex needs a few more escapes:
.
withinvnd.elasticsearch+
Escape the/
between the type and subtype(application|text)\\/(vnd\\.elasticsearch\\+)?([^;]+)(\\s*;\\s*compatible-with=(\\d+))?
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.
Something I am curious about is the
(\\d+))?
at the end. If possible, it would be nice to define this as part of our "spec". This would be something we could be strict (eschew leniency!) about, such as enforcing it to be in a specific format (and throwing an exception when someone specifies a pattern that is not correct. We can also put the format into the documentation.If you agree with the sentiment, I would be curious what we expect the final number to be, for example, which of these do we intend to support (now and in the future):
Then I think we should make this strict so users find out early if they've send an illegal 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.
@sethmlarson Why does the forward slash needs escaping? It's not special in Java?
I wondering if we should anchor the pattern with
^
at the start and$
at the end, to be on the safe side?In
([^;]+)(\\s*;
, the[^;]+
will also consume whitespace, so I believe you can remove the\\s*
.Can we switch any of these capture groups to non-capturing if we don't actually need the contents, just the grouping? i.e
(?:<pattern>)
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.
@pugnascotia You're right the
/
doesn't need an escape in JavaThere 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.
@sethmlarson fixed the
\\.
@dakrone Only major versions are supported, hence only digits are allowed - representing the major part of the version #51816 (no dots or any other character, preventing parsing minor version)
Good point on validation - how much should be done, how precise the exception should be.. @jakelandis or @jaymode any views on this?
@pugnascotia good point on consuming a space. The subtype (json, yaml, etc) are not allowing spaces. Added the anchoring and skipped capturing when possible. I think all of the groups require capturing though (except maybe for the optional
; charset=UTF-8
)