-
Notifications
You must be signed in to change notification settings - Fork 25k
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
Changes from all commits
5e42390
01f001d
50a88a0
fb03ffd
09f281e
8bc024f
070508c
3c7ab16
59a7f42
968b1c9
46f8f33
cbbe093
222caee
6bdec13
ee97564
7f52e11
63db70c
57ddb40
b5f1eff
90e798d
fa49be4
31d92ac
a925fbe
23c4e41
b1e3fb1
c17a895
7d6bd08
3c93954
77068a8
8fb0cd4
63fb6c7
4598a0a
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 |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/* | ||
* Licensed to Elasticsearch under one or more contributor | ||
* license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright | ||
* ownership. Elasticsearch licenses this file to you under | ||
* the Apache License, Version 2.0 (the "License"); you may | ||
* not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
package org.elasticsearch.common.xcontent; | ||
|
||
/** | ||
* Abstracts a <a href="http://en.wikipedia.org/wiki/Internet_media_type">Media Type</a> and a format parameter. | ||
* Media types are used as values on Content-Type and Accept headers | ||
* format is an URL parameter, specifies response media type. | ||
*/ | ||
public interface MediaType { | ||
/** | ||
* Returns a type part of a MediaType | ||
* i.e. application for application/json | ||
*/ | ||
String type(); | ||
|
||
/** | ||
* Returns a subtype part of a MediaType. | ||
* i.e. json for application/json | ||
*/ | ||
String subtype(); | ||
|
||
/** | ||
* 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 has a subtype of text but format is txt | ||
*/ | ||
String format(); | ||
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. Format, shortName, and subType seem like 3 ways to represent the same thing, and would be great if we could get to a common term ( 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. in some cases these 3 are actually different. For instance - I think shortName is redundant and I will give it a go to remove it |
||
|
||
/** | ||
* returns a string representation of a media type. | ||
*/ | ||
default String typeWithSubtype(){ | ||
return type() + "/" + subtype(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
/* | ||
* Licensed to Elasticsearch under one or more contributor | ||
* license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright | ||
* ownership. Elasticsearch licenses this file to you under | ||
* the Apache License, Version 2.0 (the "License"); you may | ||
* not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
package org.elasticsearch.common.xcontent; | ||
|
||
import java.util.HashMap; | ||
import java.util.Locale; | ||
import java.util.Map; | ||
|
||
public class MediaTypeParser<T extends MediaType> { | ||
private final Map<String, T> formatToMediaType; | ||
private final Map<String, T> typeWithSubtypeToMediaType; | ||
|
||
public MediaTypeParser(T[] acceptedMediaTypes) { | ||
this(acceptedMediaTypes, Map.of()); | ||
} | ||
|
||
public MediaTypeParser(T[] acceptedMediaTypes, Map<String, T> additionalMediaTypes) { | ||
final int size = acceptedMediaTypes.length + additionalMediaTypes.size(); | ||
Map<String, T> formatMap = new HashMap<>(size); | ||
Map<String, T> typeMap = new HashMap<>(size); | ||
for (T mediaType : acceptedMediaTypes) { | ||
typeMap.put(mediaType.typeWithSubtype(), mediaType); | ||
formatMap.put(mediaType.format(), mediaType); | ||
} | ||
for (Map.Entry<String, T> entry : additionalMediaTypes.entrySet()) { | ||
String typeWithSubtype = entry.getKey(); | ||
T mediaType = entry.getValue(); | ||
|
||
typeMap.put(typeWithSubtype.toLowerCase(Locale.ROOT), mediaType); | ||
formatMap.put(mediaType.format(), mediaType); | ||
} | ||
|
||
this.formatToMediaType = Map.copyOf(formatMap); | ||
this.typeWithSubtypeToMediaType = Map.copyOf(typeMap); | ||
} | ||
|
||
public T fromMediaType(String mediaType) { | ||
ParsedMediaType parsedMediaType = parseMediaType(mediaType); | ||
return parsedMediaType != null ? parsedMediaType.getMediaType() : null; | ||
} | ||
|
||
public T fromFormat(String format) { | ||
if (format == null) { | ||
return null; | ||
} | ||
return formatToMediaType.get(format.toLowerCase(Locale.ROOT)); | ||
} | ||
|
||
/** | ||
* parsing media type that follows https://tools.ietf.org/html/rfc7231#section-3.1.1.1 | ||
* @param headerValue a header value from Accept or Content-Type | ||
* @return a parsed media-type | ||
*/ | ||
public ParsedMediaType parseMediaType(String headerValue) { | ||
if (headerValue != null) { | ||
String[] split = headerValue.toLowerCase(Locale.ROOT).split(";"); | ||
|
||
String[] typeSubtype = split[0].trim().toLowerCase(Locale.ROOT) | ||
.split("/"); | ||
if (typeSubtype.length == 2) { | ||
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. I'm not sure I completely follow the The previous version accepted either variants (format ( I see two cases Is possible to just support the mime type for all content-type and accept header parsing ? (leaving 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. 4x yes. 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. thanks.. i double checked I have not validated 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.
|
||
String type = typeSubtype[0]; | ||
String subtype = typeSubtype[1]; | ||
T xContentType = typeWithSubtypeToMediaType.get(type + "/" + subtype); | ||
if (xContentType != null) { | ||
Map<String, String> parameters = new HashMap<>(); | ||
for (int i = 1; i < split.length; i++) { | ||
//spaces are allowed between parameters, but not between '=' sign | ||
String[] keyValueParam = split[i].trim().split("="); | ||
if (keyValueParam.length != 2 || hasSpaces(keyValueParam[0]) || hasSpaces(keyValueParam[1])) { | ||
return null; | ||
} | ||
parameters.put(keyValueParam[0].toLowerCase(Locale.ROOT), keyValueParam[1].toLowerCase(Locale.ROOT)); | ||
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. I think you need to check for 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. good point, will add test cases |
||
} | ||
return new ParsedMediaType(xContentType, parameters); | ||
} | ||
} | ||
|
||
} | ||
return null; | ||
} | ||
|
||
private boolean hasSpaces(String s) { | ||
return s.trim().equals(s) == false; | ||
} | ||
|
||
/** | ||
* A media type object that contains all the information provided on a Content-Type or Accept header | ||
*/ | ||
public class ParsedMediaType { | ||
private final Map<String, String> parameters; | ||
private final T mediaType; | ||
|
||
public ParsedMediaType(T mediaType, Map<String, String> parameters) { | ||
this.parameters = parameters; | ||
this.mediaType = mediaType; | ||
} | ||
|
||
public T getMediaType() { | ||
return mediaType; | ||
} | ||
|
||
public Map<String, String> getParameters() { | ||
return parameters; | ||
} | ||
} | ||
} |
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?