Merge remote-tracking branch 'origin/jetty-9.3.x' into jetty-9.4.x
This commit is contained in:
commit
c6e910cf12
|
@ -41,11 +41,18 @@ import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/** MIME Type enum and utilities
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class MimeTypes
|
public class MimeTypes
|
||||||
{
|
{
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
private static final Logger LOG = Log.getLogger(MimeTypes.class);
|
||||||
|
private static final Trie<ByteBuffer> TYPES= new ArrayTrie<ByteBuffer>(512);
|
||||||
|
private static final Map<String,String> __dftMimeMap = new HashMap<String,String>();
|
||||||
|
private static final Map<String,String> __inferredEncodings = new HashMap<String,String>();
|
||||||
|
private static final Map<String,String> __assumedEncodings = new HashMap<String,String>();
|
||||||
|
|
||||||
public enum Type
|
public enum Type
|
||||||
{
|
{
|
||||||
FORM_ENCODED("application/x-www-form-urlencoded"),
|
FORM_ENCODED("application/x-www-form-urlencoded"),
|
||||||
|
@ -70,8 +77,8 @@ public class MimeTypes
|
||||||
TEXT_JSON_8859_1("text/json;charset=iso-8859-1",TEXT_JSON),
|
TEXT_JSON_8859_1("text/json;charset=iso-8859-1",TEXT_JSON),
|
||||||
TEXT_JSON_UTF_8("text/json;charset=utf-8",TEXT_JSON),
|
TEXT_JSON_UTF_8("text/json;charset=utf-8",TEXT_JSON),
|
||||||
|
|
||||||
APPLICATION_JSON_8859_1("text/json;charset=iso-8859-1",APPLICATION_JSON),
|
APPLICATION_JSON_8859_1("application/json;charset=iso-8859-1",APPLICATION_JSON),
|
||||||
APPLICATION_JSON_UTF_8("text/json;charset=utf-8",APPLICATION_JSON);
|
APPLICATION_JSON_UTF_8("application/json;charset=utf-8",APPLICATION_JSON);
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -177,12 +184,7 @@ public class MimeTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
private static final Logger LOG = Log.getLogger(MimeTypes.class);
|
public static final Trie<MimeTypes.Type> CACHE= new ArrayTrie<>(512);
|
||||||
public final static Trie<MimeTypes.Type> CACHE= new ArrayTrie<>(512);
|
|
||||||
private final static Trie<ByteBuffer> TYPES= new ArrayTrie<ByteBuffer>(512);
|
|
||||||
private final static Map<String,String> __dftMimeMap = new HashMap<String,String>();
|
|
||||||
private final static Map<String,String> __encodings = new HashMap<String,String>();
|
|
||||||
|
|
||||||
static
|
static
|
||||||
{
|
{
|
||||||
for (MimeTypes.Type type : MimeTypes.Type.values())
|
for (MimeTypes.Type type : MimeTypes.Type.values())
|
||||||
|
@ -197,6 +199,9 @@ public class MimeTypes
|
||||||
CACHE.put(alt,type);
|
CACHE.put(alt,type);
|
||||||
TYPES.put(alt,type.asBuffer());
|
TYPES.put(alt,type.asBuffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type.isCharsetAssumed())
|
||||||
|
__assumedEncodings.put(type.asString(),type.getCharsetString());
|
||||||
}
|
}
|
||||||
|
|
||||||
String resourceName = "org/eclipse/jetty/http/mime.properties";
|
String resourceName = "org/eclipse/jetty/http/mime.properties";
|
||||||
|
@ -240,7 +245,6 @@ public class MimeTypes
|
||||||
LOG.debug(e);
|
LOG.debug(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
resourceName = "org/eclipse/jetty/http/encoding.properties";
|
resourceName = "org/eclipse/jetty/http/encoding.properties";
|
||||||
try (InputStream stream = MimeTypes.class.getClassLoader().getResourceAsStream(resourceName))
|
try (InputStream stream = MimeTypes.class.getClassLoader().getResourceAsStream(resourceName))
|
||||||
{
|
{
|
||||||
|
@ -254,13 +258,20 @@ public class MimeTypes
|
||||||
props.load(reader);
|
props.load(reader);
|
||||||
props.stringPropertyNames().stream()
|
props.stringPropertyNames().stream()
|
||||||
.filter(t->t!=null)
|
.filter(t->t!=null)
|
||||||
.forEach(t->__encodings.put(t, props.getProperty(t)));
|
.forEach(t->
|
||||||
|
{
|
||||||
|
String charset = props.getProperty(t);
|
||||||
|
if (charset.startsWith("-"))
|
||||||
|
__assumedEncodings.put(t, charset.substring(1));
|
||||||
|
else
|
||||||
|
__inferredEncodings.put(t, props.getProperty(t));
|
||||||
|
});
|
||||||
|
|
||||||
if (__encodings.size()==0)
|
if (__inferredEncodings.size()==0)
|
||||||
{
|
{
|
||||||
LOG.warn("Empty encodings at {}", resourceName);
|
LOG.warn("Empty encodings at {}", resourceName);
|
||||||
}
|
}
|
||||||
else if (__encodings.size()<props.keySet().size())
|
else if ((__inferredEncodings.size()+__assumedEncodings.size())<props.keySet().size())
|
||||||
{
|
{
|
||||||
LOG.warn("Null or duplicate encodings in resource: {}", resourceName);
|
LOG.warn("Null or duplicate encodings in resource: {}", resourceName);
|
||||||
}
|
}
|
||||||
|
@ -312,6 +323,43 @@ public class MimeTypes
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/** Get the MIME type by filename extension.
|
/** Get the MIME type by filename extension.
|
||||||
|
* Lookup only the static default mime map.
|
||||||
|
* @param filename A file name
|
||||||
|
* @return MIME type matching the longest dot extension of the
|
||||||
|
* file name.
|
||||||
|
*/
|
||||||
|
public static String getDefaultMimeByExtension(String filename)
|
||||||
|
{
|
||||||
|
String type=null;
|
||||||
|
|
||||||
|
if (filename!=null)
|
||||||
|
{
|
||||||
|
int i=-1;
|
||||||
|
while(type==null)
|
||||||
|
{
|
||||||
|
i=filename.indexOf(".",i+1);
|
||||||
|
|
||||||
|
if (i<0 || i>=filename.length())
|
||||||
|
break;
|
||||||
|
|
||||||
|
String ext=StringUtil.asciiToLowerCase(filename.substring(i+1));
|
||||||
|
if (type==null)
|
||||||
|
type=__dftMimeMap.get(ext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type==null)
|
||||||
|
{
|
||||||
|
if (type==null)
|
||||||
|
type=__dftMimeMap.get("*");
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/** Get the MIME type by filename extension.
|
||||||
|
* Lookup the content and static default mime maps.
|
||||||
* @param filename A file name
|
* @param filename A file name
|
||||||
* @return MIME type matching the longest dot extension of the
|
* @return MIME type matching the longest dot extension of the
|
||||||
* file name.
|
* file name.
|
||||||
|
@ -449,11 +497,44 @@ public class MimeTypes
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String inferCharsetFromContentType(String value)
|
/**
|
||||||
|
* Access a mutable map of mime type to the charset inferred from that content type.
|
||||||
|
* An inferred encoding is used by when encoding/decoding a stream and is
|
||||||
|
* explicitly set in any metadata (eg Content-Type).
|
||||||
|
* @return Map of mime type to charset
|
||||||
|
*/
|
||||||
|
public static Map<String,String> getInferredEncodings()
|
||||||
{
|
{
|
||||||
return __encodings.get(value);
|
return __inferredEncodings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access a mutable map of mime type to the charset assumed for that content type.
|
||||||
|
* An assumed encoding is used by when encoding/decoding a stream, but is not
|
||||||
|
* explicitly set in any metadata (eg Content-Type).
|
||||||
|
* @return Map of mime type to charset
|
||||||
|
*/
|
||||||
|
public static Map<String,String> getAssumedEncodings()
|
||||||
|
{
|
||||||
|
return __inferredEncodings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public static String inferCharsetFromContentType(String contentType)
|
||||||
|
{
|
||||||
|
return getCharsetAssumedFromContentType(contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getCharsetInferredFromContentType(String contentType)
|
||||||
|
{
|
||||||
|
return __inferredEncodings.get(contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getCharsetAssumedFromContentType(String contentType)
|
||||||
|
{
|
||||||
|
return __assumedEncodings.get(contentType);
|
||||||
|
}
|
||||||
|
|
||||||
public static String getContentTypeWithoutCharset(String value)
|
public static String getContentTypeWithoutCharset(String value)
|
||||||
{
|
{
|
||||||
int end=value.length();
|
int end=value.length();
|
||||||
|
@ -545,4 +626,5 @@ public class MimeTypes
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
|
# Mapping of mime type to inferred or assumed charset
|
||||||
|
# inferred charsets are used for encoding/decoding and explicitly set in associated metadata
|
||||||
|
# assumed charsets are used for encoding/decoding, but are not set in associated metadata
|
||||||
|
# In this file, assumed charsets are indicatd with a leading '-'
|
||||||
|
|
||||||
text/html=utf-8
|
text/html=utf-8
|
||||||
text/plain=iso-8859-1
|
text/plain=iso-8859-1
|
||||||
text/xml=utf-8
|
text/xml=utf-8
|
||||||
text/json=utf-8
|
application/xhtml+xml=utf-8
|
||||||
application/xhtml+xml=utf-8
|
text/json=-utf-8
|
||||||
|
application/vnd.api+json=-utf-8
|
|
@ -797,7 +797,12 @@ public class Response implements HttpServletResponse
|
||||||
public String getCharacterEncoding()
|
public String getCharacterEncoding()
|
||||||
{
|
{
|
||||||
if (_characterEncoding == null)
|
if (_characterEncoding == null)
|
||||||
|
{
|
||||||
|
String encoding = MimeTypes.getCharsetAssumedFromContentType(_contentType);
|
||||||
|
if (encoding!=null)
|
||||||
|
return encoding;
|
||||||
_characterEncoding = StringUtil.__ISO_8859_1;
|
_characterEncoding = StringUtil.__ISO_8859_1;
|
||||||
|
}
|
||||||
return _characterEncoding;
|
return _characterEncoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -837,10 +842,14 @@ public class Response implements HttpServletResponse
|
||||||
encoding=_mimeType.getCharsetString();
|
encoding=_mimeType.getCharsetString();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
encoding = MimeTypes.inferCharsetFromContentType(_contentType);
|
encoding = MimeTypes.getCharsetAssumedFromContentType(_contentType);
|
||||||
if (encoding == null)
|
if (encoding == null)
|
||||||
encoding = StringUtil.__ISO_8859_1;
|
{
|
||||||
setCharacterEncoding(encoding,EncodingFrom.INFERRED);
|
encoding = MimeTypes.getCharsetInferredFromContentType(_contentType);
|
||||||
|
if (encoding == null)
|
||||||
|
encoding = StringUtil.__ISO_8859_1;
|
||||||
|
setCharacterEncoding(encoding,EncodingFrom.INFERRED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -150,7 +150,7 @@ public class BufferedResponseHandler extends HandlerWrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the mime type is known from the path, then apply mime type filtering
|
// If the mime type is known from the path, then apply mime type filtering
|
||||||
String mimeType = context==null?null:context.getMimeType(path);
|
String mimeType = context==null?MimeTypes.getDefaultMimeByExtension(path):context.getMimeType(path);
|
||||||
if (mimeType!=null)
|
if (mimeType!=null)
|
||||||
{
|
{
|
||||||
mimeType = MimeTypes.getContentTypeWithoutCharset(mimeType);
|
mimeType = MimeTypes.getContentTypeWithoutCharset(mimeType);
|
||||||
|
|
|
@ -20,9 +20,12 @@ package org.eclipse.jetty.server.handler.gzip;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.zip.Deflater;
|
import java.util.zip.Deflater;
|
||||||
|
|
||||||
|
import javax.servlet.DispatcherType;
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
@ -66,6 +69,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
private boolean _checkGzExists = true;
|
private boolean _checkGzExists = true;
|
||||||
private boolean _syncFlush = false;
|
private boolean _syncFlush = false;
|
||||||
private int _inflateBufferSize = -1;
|
private int _inflateBufferSize = -1;
|
||||||
|
private EnumSet<DispatcherType> _dispatchers = EnumSet.of(DispatcherType.REQUEST);
|
||||||
|
|
||||||
// non-static, as other GzipHandler instances may have different configurations
|
// non-static, as other GzipHandler instances may have different configurations
|
||||||
private final ThreadLocal<Deflater> _deflater = new ThreadLocal<>();
|
private final ThreadLocal<Deflater> _deflater = new ThreadLocal<>();
|
||||||
|
@ -130,6 +134,25 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
_methods.exclude(m);
|
_methods.exclude(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
public EnumSet<DispatcherType> getDispatcherTypes()
|
||||||
|
{
|
||||||
|
return _dispatchers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
public void setDispatcherTypes(EnumSet<DispatcherType> dispatchers)
|
||||||
|
{
|
||||||
|
_dispatchers = dispatchers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
public void setDispatcherTypes(DispatcherType... dispatchers)
|
||||||
|
{
|
||||||
|
_dispatchers = EnumSet.copyOf(Arrays.asList(dispatchers));
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* Set the mime types.
|
* Set the mime types.
|
||||||
|
@ -395,6 +418,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
return _minGzipSize;
|
return _minGzipSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
protected HttpField getVaryField()
|
protected HttpField getVaryField()
|
||||||
{
|
{
|
||||||
return _vary;
|
return _vary;
|
||||||
|
@ -429,6 +453,13 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
String path = context==null?baseRequest.getRequestURI():URIUtil.addPaths(baseRequest.getServletPath(),baseRequest.getPathInfo());
|
String path = context==null?baseRequest.getRequestURI():URIUtil.addPaths(baseRequest.getServletPath(),baseRequest.getPathInfo());
|
||||||
LOG.debug("{} handle {} in {}",this,baseRequest,context);
|
LOG.debug("{} handle {} in {}",this,baseRequest,context);
|
||||||
|
|
||||||
|
if (!_dispatchers.contains(baseRequest.getDispatcherType()))
|
||||||
|
{
|
||||||
|
LOG.debug("{} excluded by dispatcherType {}",this,baseRequest.getDispatcherType());
|
||||||
|
_handler.handle(target,baseRequest, request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle request inflation
|
// Handle request inflation
|
||||||
if (_inflateBufferSize>0)
|
if (_inflateBufferSize>0)
|
||||||
{
|
{
|
||||||
|
@ -442,8 +473,8 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpOutput out = baseRequest.getResponse().getHttpOutput();
|
|
||||||
// Are we already being gzipped?
|
// Are we already being gzipped?
|
||||||
|
HttpOutput out = baseRequest.getResponse().getHttpOutput();
|
||||||
HttpOutput.Interceptor interceptor = out.getInterceptor();
|
HttpOutput.Interceptor interceptor = out.getInterceptor();
|
||||||
while (interceptor!=null)
|
while (interceptor!=null)
|
||||||
{
|
{
|
||||||
|
@ -474,7 +505,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exclude non compressible mime-types known from URI extension. - no Vary because no matter what client, this URI is always excluded
|
// Exclude non compressible mime-types known from URI extension. - no Vary because no matter what client, this URI is always excluded
|
||||||
String mimeType = context==null?null:context.getMimeType(path);
|
String mimeType = context==null?MimeTypes.getDefaultMimeByExtension(path):context.getMimeType(path);
|
||||||
if (mimeType!=null)
|
if (mimeType!=null)
|
||||||
{
|
{
|
||||||
mimeType = MimeTypes.getContentTypeWithoutCharset(mimeType);
|
mimeType = MimeTypes.getContentTypeWithoutCharset(mimeType);
|
||||||
|
|
|
@ -278,6 +278,45 @@ public class ResponseTest
|
||||||
response.getWriter();
|
response.getWriter();
|
||||||
assertEquals("application/json",response.getContentType());
|
assertEquals("application/json",response.getContentType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInferredCharset() throws Exception
|
||||||
|
{
|
||||||
|
// Inferred from encoding.properties
|
||||||
|
Response response = getResponse();
|
||||||
|
|
||||||
|
assertEquals(null, response.getContentType());
|
||||||
|
|
||||||
|
response.setHeader("Content-Type", "application/xhtml+xml");
|
||||||
|
assertEquals("application/xhtml+xml", response.getContentType());
|
||||||
|
response.getWriter();
|
||||||
|
assertEquals("application/xhtml+xml;charset=utf-8", response.getContentType());
|
||||||
|
assertEquals("utf-8", response.getCharacterEncoding());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAssumedCharset() throws Exception
|
||||||
|
{
|
||||||
|
Response response = getResponse();
|
||||||
|
|
||||||
|
// Assumed from known types
|
||||||
|
assertEquals(null, response.getContentType());
|
||||||
|
response.setHeader("Content-Type", "text/json");
|
||||||
|
assertEquals("text/json", response.getContentType());
|
||||||
|
response.getWriter();
|
||||||
|
assertEquals("text/json", response.getContentType());
|
||||||
|
assertEquals("utf-8", response.getCharacterEncoding());
|
||||||
|
|
||||||
|
response.recycle();
|
||||||
|
|
||||||
|
// Assumed from encoding.properties
|
||||||
|
assertEquals(null, response.getContentType());
|
||||||
|
response.setHeader("Content-Type", "application/vnd.api+json");
|
||||||
|
assertEquals("application/vnd.api+json", response.getContentType());
|
||||||
|
response.getWriter();
|
||||||
|
assertEquals("application/vnd.api+json", response.getContentType());
|
||||||
|
assertEquals("utf-8", response.getCharacterEncoding());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStrangeContentType() throws Exception
|
public void testStrangeContentType() throws Exception
|
||||||
|
|
Loading…
Reference in New Issue