Merge remote-tracking branch 'origin/jetty-9.3.x' into jetty-9.4.x

This commit is contained in:
Greg Wilkins 2017-01-18 14:39:20 +11:00
commit c6e910cf12
6 changed files with 190 additions and 23 deletions

View File

@ -41,11 +41,18 @@ import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/**
/** MIME Type enum and utilities
*
*/
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
{
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_UTF_8("text/json;charset=utf-8",TEXT_JSON),
APPLICATION_JSON_8859_1("text/json;charset=iso-8859-1",APPLICATION_JSON),
APPLICATION_JSON_UTF_8("text/json;charset=utf-8",APPLICATION_JSON);
APPLICATION_JSON_8859_1("application/json;charset=iso-8859-1",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 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>();
public static final Trie<MimeTypes.Type> CACHE= new ArrayTrie<>(512);
static
{
for (MimeTypes.Type type : MimeTypes.Type.values())
@ -197,6 +199,9 @@ public class MimeTypes
CACHE.put(alt,type);
TYPES.put(alt,type.asBuffer());
}
if (type.isCharsetAssumed())
__assumedEncodings.put(type.asString(),type.getCharsetString());
}
String resourceName = "org/eclipse/jetty/http/mime.properties";
@ -240,7 +245,6 @@ public class MimeTypes
LOG.debug(e);
}
resourceName = "org/eclipse/jetty/http/encoding.properties";
try (InputStream stream = MimeTypes.class.getClassLoader().getResourceAsStream(resourceName))
{
@ -254,13 +258,20 @@ public class MimeTypes
props.load(reader);
props.stringPropertyNames().stream()
.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);
}
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);
}
@ -312,6 +323,43 @@ public class MimeTypes
/* ------------------------------------------------------------ */
/** 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
* @return MIME type matching the longest dot extension of the
* file name.
@ -449,11 +497,44 @@ public class MimeTypes
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)
{
int end=value.length();
@ -545,4 +626,5 @@ public class MimeTypes
return builder.toString();
}
}

View File

@ -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/plain=iso-8859-1
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

View File

@ -797,7 +797,12 @@ public class Response implements HttpServletResponse
public String getCharacterEncoding()
{
if (_characterEncoding == null)
{
String encoding = MimeTypes.getCharsetAssumedFromContentType(_contentType);
if (encoding!=null)
return encoding;
_characterEncoding = StringUtil.__ISO_8859_1;
}
return _characterEncoding;
}
@ -837,10 +842,14 @@ public class Response implements HttpServletResponse
encoding=_mimeType.getCharsetString();
else
{
encoding = MimeTypes.inferCharsetFromContentType(_contentType);
encoding = MimeTypes.getCharsetAssumedFromContentType(_contentType);
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);
}
}
}

View File

@ -150,7 +150,7 @@ public class BufferedResponseHandler extends HandlerWrapper
}
// 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)
{
mimeType = MimeTypes.getContentTypeWithoutCharset(mimeType);

View File

@ -20,9 +20,12 @@ package org.eclipse.jetty.server.handler.gzip;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Set;
import java.util.zip.Deflater;
import javax.servlet.DispatcherType;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@ -66,6 +69,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
private boolean _checkGzExists = true;
private boolean _syncFlush = false;
private int _inflateBufferSize = -1;
private EnumSet<DispatcherType> _dispatchers = EnumSet.of(DispatcherType.REQUEST);
// non-static, as other GzipHandler instances may have different configurations
private final ThreadLocal<Deflater> _deflater = new ThreadLocal<>();
@ -130,6 +134,25 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
_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.
@ -395,6 +418,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
return _minGzipSize;
}
/* ------------------------------------------------------------ */
protected HttpField getVaryField()
{
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());
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
if (_inflateBufferSize>0)
{
@ -442,8 +473,8 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
}
}
HttpOutput out = baseRequest.getResponse().getHttpOutput();
// Are we already being gzipped?
HttpOutput out = baseRequest.getResponse().getHttpOutput();
HttpOutput.Interceptor interceptor = out.getInterceptor();
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
String mimeType = context==null?null:context.getMimeType(path);
String mimeType = context==null?MimeTypes.getDefaultMimeByExtension(path):context.getMimeType(path);
if (mimeType!=null)
{
mimeType = MimeTypes.getContentTypeWithoutCharset(mimeType);

View File

@ -278,6 +278,45 @@ public class ResponseTest
response.getWriter();
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
public void testStrangeContentType() throws Exception