Skip to content

Commit c219e17

Browse files
authored
Introduce per REST endpoint media types (elastic#64406)
Per REST endpoint media types declaration allows to make parsing/validation more strict. If a media type was declared only in one endpoint (for instance CSV in SQL endpoint) it should not be allowed to parse that media type when using other endpoints. However, the Compatible API need to be able to understand all media types supported by Elasticsearch in order to parse a compatible-with=version parameter. This implies that endpoints need to declare which media type they support and how to parse them (if introducing new media types - like SQL). How to parse: MediaType interface still serves as an abstraction on top of XContentType and TextFormat. It also has a declaration of mappings String-MediaType with parameters. Parameters declares the names of parameters and regex to validate its values. This instructs how to perform the parsing. For instance - XContentType.JSON has the mapping of application/vnd.elasticsearch+json -> JSON and allows parameters compatible-with=\d and charset=utf-8 MediaTypeParser was simplified into ParsedMediaType class with static factory method for parsing. How to declare: RestHandler interface is extended with a validAcceptMediaTypes which returns a MediaTypeRegistry - a class that encapsulates mappings of string (type/subtype) to MediaType, allowed parameters and formatPathParameter values. We only need to allow of declaration of valid media types for Accept header. Content-Type valid media types are fixed to XContentType instances - json, yaml, smile, cbor. relates elastic#51816
1 parent b71bd95 commit c219e17

File tree

24 files changed

+576
-389
lines changed

24 files changed

+576
-389
lines changed

libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaType.java

+26-15
Original file line numberDiff line numberDiff line change
@@ -19,34 +19,45 @@
1919

2020
package org.elasticsearch.common.xcontent;
2121

22+
import org.elasticsearch.common.collect.Tuple;
23+
24+
import java.util.Collections;
25+
import java.util.Map;
26+
import java.util.Set;
27+
2228
/**
23-
* Abstracts a <a href="http://en.wikipedia.org/wiki/Internet_media_type">Media Type</a> and a format parameter.
29+
* Abstracts a <a href="http://en.wikipedia.org/wiki/Internet_media_type">Media Type</a> and a query parameter <code>format</code>.
2430
* Media types are used as values on Content-Type and Accept headers
2531
* format is an URL parameter, specifies response media type.
2632
*/
2733
public interface MediaType {
28-
/**
29-
* Returns a type part of a MediaType
30-
* i.e. application for application/json
31-
*/
32-
String type();
34+
String COMPATIBLE_WITH_PARAMETER_NAME = "compatible-with";
35+
String VERSION_PATTERN = "\\d+";
3336

3437
/**
35-
* Returns a subtype part of a MediaType.
36-
* i.e. json for application/json
38+
* Returns a corresponding format path parameter for a MediaType.
39+
* i.e. ?format=txt for plain/text media type
3740
*/
38-
String subtype();
41+
String queryParameter();
3942

4043
/**
41-
* Returns a corresponding format for a MediaType. i.e. json for application/json media type
42-
* Can differ from the MediaType's subtype i.e plain/text has a subtype of text but format is txt
44+
* Returns a set of HeaderValues - allowed media type values on Accept or Content-Type headers
45+
* Also defines media type parameters for validation.
4346
*/
44-
String format();
47+
Set<HeaderValue> headerValues();
4548

4649
/**
47-
* returns a string representation of a media type.
50+
* A class to represent supported mediaType values i.e. application/json and parameters to be validated.
51+
* Parameters for validation is a map where a key is a parameter name, value is a parameter regex which is used for validation.
52+
* Regex will be applied with case insensitivity.
4853
*/
49-
default String typeWithSubtype(){
50-
return type() + "/" + subtype();
54+
class HeaderValue extends Tuple<String, Map<String, String>> {
55+
public HeaderValue(String headerValue, Map<String, String> parametersForValidation) {
56+
super(headerValue, parametersForValidation);
57+
}
58+
59+
public HeaderValue(String headerValue) {
60+
this(headerValue, Collections.emptyMap());
61+
}
5162
}
5263
}

libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaTypeParser.java

-160
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.common.xcontent;
21+
22+
import java.util.HashMap;
23+
import java.util.Locale;
24+
import java.util.Map;
25+
import java.util.Set;
26+
import java.util.regex.Pattern;
27+
28+
/**
29+
* A registry for quick media type lookup.
30+
* It allows to find media type by a header value - typeWithSubtype aka mediaType without parameters.
31+
* I.e. application/json will return XContentType.JSON
32+
* Also allows to find media type by a path parameter <code>format</code>.
33+
* I.e. txt used in path _sql?format=txt will return TextFormat.PLAIN_TEXT
34+
*
35+
* Multiple header representations may map to a single {@link MediaType} for example, "application/json"
36+
* and "application/vnd.elasticsearch+json" both represent a JSON MediaType.
37+
* A MediaType can have only one query parameter representation.
38+
* For example "json" (case insensitive) maps back to a JSON media type.
39+
*
40+
* Additionally, a http header may optionally have parameters. For example "application/json; charset=utf-8".
41+
* This class also allows to define a regular expression for valid values of charset.
42+
*/
43+
public class MediaTypeRegistry<T extends MediaType> {
44+
45+
private Map<String, T> queryParamToMediaType = new HashMap<>();
46+
private Map<String, T> typeWithSubtypeToMediaType = new HashMap<>();
47+
private Map<String, Map<String, Pattern>> parametersMap = new HashMap<>();
48+
49+
public T queryParamToMediaType(String format) {
50+
if (format == null) {
51+
return null;
52+
}
53+
return queryParamToMediaType.get(format.toLowerCase(Locale.ROOT));
54+
}
55+
56+
public T typeWithSubtypeToMediaType(String typeWithSubtype) {
57+
return typeWithSubtypeToMediaType.get(typeWithSubtype.toLowerCase(Locale.ROOT));
58+
}
59+
60+
public Map<String, Pattern> parametersFor(String typeWithSubtype) {
61+
return parametersMap.get(typeWithSubtype);
62+
}
63+
64+
public MediaTypeRegistry<T> register(T[] mediaTypes ) {
65+
for (T mediaType : mediaTypes) {
66+
Set<MediaType.HeaderValue> tuples = mediaType.headerValues();
67+
for (MediaType.HeaderValue headerValue : tuples) {
68+
queryParamToMediaType.put(mediaType.queryParameter(), mediaType);
69+
typeWithSubtypeToMediaType.put(headerValue.v1(), mediaType);
70+
parametersMap.put(headerValue.v1(), convertPatterns(headerValue.v2()));
71+
}
72+
}
73+
return this;
74+
}
75+
76+
private Map<String,Pattern> convertPatterns(Map<String, String> paramNameAndValueRegex) {
77+
Map<String, Pattern> parametersForMediaType = new HashMap<>(paramNameAndValueRegex.size());
78+
for (Map.Entry<String, String> params : paramNameAndValueRegex.entrySet()) {
79+
String parameterName = params.getKey().toLowerCase(Locale.ROOT);
80+
String parameterRegex = params.getValue();
81+
Pattern pattern = Pattern.compile(parameterRegex, Pattern.CASE_INSENSITIVE);
82+
parametersForMediaType.put(parameterName, pattern);
83+
}
84+
return parametersForMediaType;
85+
}
86+
}

0 commit comments

Comments
 (0)