Merge remote-tracking branch 'origin/master' into servlet-3.1-api
This commit is contained in:
commit
cbac0ea5fa
|
@ -449,10 +449,12 @@ public class HttpClientStreamTest extends AbstractHttpClientServerTest
|
|||
public void testInputStreamResponseListenerFailedBeforeResponse() throws Exception
|
||||
{
|
||||
start(new EmptyServerHandler());
|
||||
int port = connector.getLocalPort();
|
||||
server.stop();
|
||||
|
||||
InputStreamResponseListener listener = new InputStreamResponseListener();
|
||||
// Connect to the wrong port
|
||||
client.newRequest("localhost", 0)
|
||||
client.newRequest("localhost", port)
|
||||
.scheme(scheme)
|
||||
.send(listener);
|
||||
Result result = listener.await(5, TimeUnit.SECONDS);
|
||||
|
|
|
@ -24,8 +24,6 @@ import java.util.Arrays;
|
|||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -68,10 +66,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
Assert.assertEquals(0, activeConnections.size());
|
||||
|
||||
final CountDownLatch headersLatch = new CountDownLatch(1);
|
||||
final CountDownLatch testLatch = new CountDownLatch(1);
|
||||
final CountDownLatch successLatch = new CountDownLatch(3);
|
||||
final AtomicBoolean failed = new AtomicBoolean(false);
|
||||
|
||||
client.newRequest(host, port)
|
||||
.scheme(scheme)
|
||||
.onRequestSuccess(new Request.SuccessListener()
|
||||
|
@ -87,15 +82,9 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
public void onHeaders(Response response)
|
||||
{
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
Assert.assertEquals(1, activeConnections.size());
|
||||
headersLatch.countDown();
|
||||
try
|
||||
{
|
||||
testLatch.await();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
})
|
||||
.send(new Response.Listener.Empty()
|
||||
|
@ -109,21 +98,16 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
failed.set(result.isFailed());
|
||||
Assert.assertFalse(result.isFailed());
|
||||
successLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS));
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
Assert.assertEquals(1, activeConnections.size());
|
||||
testLatch.countDown();
|
||||
|
||||
Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
Assert.assertEquals(1, idleConnections.size());
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
Assert.assertFalse(failed.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -143,8 +127,6 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
|
||||
final CountDownLatch beginLatch = new CountDownLatch(1);
|
||||
final CountDownLatch failureLatch = new CountDownLatch(2);
|
||||
final AtomicBoolean failed = new AtomicBoolean(false);
|
||||
|
||||
client.newRequest(host, port).scheme(scheme).listener(new Request.Listener.Empty()
|
||||
{
|
||||
@Override
|
||||
|
@ -164,7 +146,9 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
failed.set(result.isFailed());
|
||||
Assert.assertTrue(result.isFailed());
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
failureLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -174,7 +158,6 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
Assert.assertTrue(failed.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -193,9 +176,6 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
Assert.assertEquals(0, activeConnections.size());
|
||||
|
||||
final CountDownLatch successLatch = new CountDownLatch(3);
|
||||
final AtomicBoolean failed = new AtomicBoolean(false);
|
||||
final AtomicBoolean four_hundred = new AtomicBoolean(false);
|
||||
|
||||
client.newRequest(host, port)
|
||||
.scheme(scheme)
|
||||
.listener(new Request.Listener.Empty()
|
||||
|
@ -218,16 +198,16 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
public void onSuccess(Response response)
|
||||
{
|
||||
Assert.assertEquals(400, response.getStatus());
|
||||
// 400 response also come with a Connection: close,
|
||||
// so the connection is closed and removed
|
||||
four_hundred.set(response.getStatus()==400);
|
||||
successLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
failed.set(result.isFailed());
|
||||
Assert.assertFalse(result.isFailed());
|
||||
successLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -236,8 +216,6 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
Assert.assertFalse(failed.get());
|
||||
Assert.assertTrue(four_hundred.get());
|
||||
}
|
||||
|
||||
@Slow
|
||||
|
@ -258,8 +236,6 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
|
||||
final long delay = 1000;
|
||||
final CountDownLatch successLatch = new CountDownLatch(3);
|
||||
final AtomicBoolean failed = new AtomicBoolean(false);
|
||||
final AtomicBoolean four_hundred = new AtomicBoolean(false);
|
||||
client.newRequest(host, port)
|
||||
.scheme(scheme)
|
||||
.listener(new Request.Listener.Empty()
|
||||
|
@ -295,16 +271,16 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
public void onSuccess(Response response)
|
||||
{
|
||||
Assert.assertEquals(400, response.getStatus());
|
||||
// 400 response also come with a Connection: close,
|
||||
// so the connection is closed and removed
|
||||
four_hundred.set(response.getStatus()==400);
|
||||
successLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
failed.set(result.isFailed());
|
||||
Assert.assertFalse(result.isFailed());
|
||||
successLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -313,8 +289,6 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
Assert.assertFalse(failed.get());
|
||||
Assert.assertTrue(four_hundred.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -335,7 +309,6 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
server.stop();
|
||||
|
||||
final CountDownLatch failureLatch = new CountDownLatch(2);
|
||||
final AtomicBoolean failed = new AtomicBoolean(false);
|
||||
client.newRequest(host, port)
|
||||
.scheme(scheme)
|
||||
.onRequestFailure(new Request.FailureListener()
|
||||
|
@ -351,7 +324,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
failed.set(result.isFailed());
|
||||
Assert.assertTrue(result.isFailed());
|
||||
failureLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -360,7 +333,6 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
Assert.assertTrue(failed.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -387,7 +359,6 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
Assert.assertEquals(0, activeConnections.size());
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final AtomicBoolean failed = new AtomicBoolean(false);
|
||||
client.newRequest(host, port)
|
||||
.scheme(scheme)
|
||||
.send(new Response.Listener.Empty()
|
||||
|
@ -395,7 +366,9 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
failed.set(result.isFailed());
|
||||
Assert.assertFalse(result.isFailed());
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -404,7 +377,6 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
Assert.assertFalse(failed.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -438,7 +410,6 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
Log.getLogger(HttpConnection.class).info("Expecting java.lang.IllegalStateException: HttpParser{s=CLOSED,...");
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final AtomicBoolean failed = new AtomicBoolean(false);
|
||||
ByteBuffer buffer = ByteBuffer.allocate(16 * 1024 * 1024);
|
||||
Arrays.fill(buffer.array(),(byte)'x');
|
||||
client.newRequest(host, port)
|
||||
|
@ -449,7 +420,8 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
failed.set(result.isFailed());
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -458,7 +430,6 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
Assert.assertTrue(failed.get());
|
||||
server.stop();
|
||||
}
|
||||
finally
|
||||
|
|
|
@ -152,7 +152,7 @@ etc/jetty-http.xml
|
|||
|
||||
#===========================================================
|
||||
# HTTPS Connector
|
||||
# Must be used with 200-ssl.ini
|
||||
# Must be used with jetty-ssl.xml
|
||||
#-----------------------------------------------------------
|
||||
# jetty.https.port=8443
|
||||
# etc/jetty-https.xml
|
||||
|
|
|
@ -510,8 +510,11 @@ public class HttpFields implements Iterable<HttpField>
|
|||
while (e.hasMoreElements())
|
||||
{
|
||||
String value = e.nextElement();
|
||||
tok = new QuotedStringTokenizer(value, separators, false, false);
|
||||
if (tok.hasMoreElements()) return true;
|
||||
if (value!=null)
|
||||
{
|
||||
tok = new QuotedStringTokenizer(value, separators, false, false);
|
||||
if (tok.hasMoreElements()) return true;
|
||||
}
|
||||
}
|
||||
tok = null;
|
||||
return false;
|
||||
|
|
|
@ -204,8 +204,36 @@ public class HttpParser
|
|||
return _state == state;
|
||||
}
|
||||
|
||||
private static class BadMessage extends Error
|
||||
{
|
||||
private final int _code;
|
||||
private final String _message;
|
||||
|
||||
BadMessage()
|
||||
{
|
||||
this(400,null);
|
||||
}
|
||||
|
||||
BadMessage(int code)
|
||||
{
|
||||
this(code,null);
|
||||
}
|
||||
|
||||
BadMessage(String message)
|
||||
{
|
||||
this(400,message);
|
||||
}
|
||||
|
||||
BadMessage(int code,String message)
|
||||
{
|
||||
_code=code;
|
||||
_message=message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
private byte next(ByteBuffer buffer)
|
||||
private byte next(ByteBuffer buffer)
|
||||
{
|
||||
byte ch=buffer.get();
|
||||
|
||||
|
@ -213,10 +241,8 @@ public class HttpParser
|
|||
if (ch>=HttpTokens.SPACE || ch<0)
|
||||
{
|
||||
if (_cr)
|
||||
{
|
||||
badMessage(buffer,400,"Bad EOL");
|
||||
return -1;
|
||||
}
|
||||
throw new BadMessage("Bad EOL");
|
||||
|
||||
/*
|
||||
if (ch>HttpTokens.SPACE)
|
||||
System.err.println("Next "+(char)ch);
|
||||
|
@ -233,8 +259,7 @@ public class HttpParser
|
|||
if (ch==HttpTokens.LINE_FEED)
|
||||
return ch;
|
||||
|
||||
badMessage(buffer,400,"Bad EOL");
|
||||
return -1;
|
||||
throw new BadMessage("Bad EOL");
|
||||
}
|
||||
|
||||
// If it is a CR
|
||||
|
@ -249,8 +274,7 @@ public class HttpParser
|
|||
if (ch==HttpTokens.LINE_FEED)
|
||||
return ch;
|
||||
|
||||
badMessage(buffer,HttpStatus.BAD_REQUEST_400,null);
|
||||
return -1;
|
||||
throw new BadMessage();
|
||||
}
|
||||
|
||||
// Defer lookup of LF
|
||||
|
@ -260,10 +284,7 @@ public class HttpParser
|
|||
|
||||
// Only LF or TAB acceptable special characters
|
||||
if (ch!=HttpTokens.LINE_FEED && ch!=HttpTokens.TAB)
|
||||
{
|
||||
badMessage(buffer,HttpStatus.BAD_REQUEST_400,null);
|
||||
return -1;
|
||||
}
|
||||
throw new BadMessage();
|
||||
|
||||
/*
|
||||
if (ch>HttpTokens.SPACE)
|
||||
|
@ -314,8 +335,6 @@ public class HttpParser
|
|||
setState(_requestHandler!=null?State.METHOD:State.RESPONSE_VERSION);
|
||||
return false;
|
||||
}
|
||||
if (ch==-1)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -358,7 +377,7 @@ public class HttpParser
|
|||
if (_state==State.URI)
|
||||
{
|
||||
LOG.warn("URI is too large >"+_maxHeaderBytes);
|
||||
badMessage(buffer,HttpStatus.REQUEST_URI_TOO_LONG_414,null);
|
||||
throw new BadMessage(HttpStatus.REQUEST_URI_TOO_LONG_414);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -366,9 +385,8 @@ public class HttpParser
|
|||
LOG.warn("request is too large >"+_maxHeaderBytes);
|
||||
else
|
||||
LOG.warn("response is too large >"+_maxHeaderBytes);
|
||||
badMessage(buffer,HttpStatus.REQUEST_ENTITY_TOO_LARGE_413,null);
|
||||
throw new BadMessage(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (_state)
|
||||
|
@ -384,8 +402,7 @@ public class HttpParser
|
|||
}
|
||||
else if (ch < HttpTokens.SPACE && ch>=0)
|
||||
{
|
||||
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"No URI");
|
||||
return true;
|
||||
throw new BadMessage(HttpStatus.BAD_REQUEST_400,"No URI");
|
||||
}
|
||||
else
|
||||
_string.append((char)ch);
|
||||
|
@ -398,15 +415,13 @@ public class HttpParser
|
|||
_version=HttpVersion.CACHE.get(version);
|
||||
if (_version==null)
|
||||
{
|
||||
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Unknown Version");
|
||||
return true;
|
||||
throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Unknown Version");
|
||||
}
|
||||
setState(State.SPACE1);
|
||||
}
|
||||
else if (ch < HttpTokens.SPACE && ch>=0)
|
||||
{
|
||||
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"No Status");
|
||||
return true;
|
||||
throw new BadMessage(HttpStatus.BAD_REQUEST_400,"No Status");
|
||||
}
|
||||
else
|
||||
_string.append((char)ch);
|
||||
|
@ -440,8 +455,7 @@ public class HttpParser
|
|||
if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
|
||||
{
|
||||
LOG.warn("URI is too large >"+_maxHeaderBytes);
|
||||
badMessage(buffer,HttpStatus.REQUEST_URI_TOO_LONG_414,null);
|
||||
return true;
|
||||
throw new BadMessage(HttpStatus.REQUEST_URI_TOO_LONG_414);
|
||||
}
|
||||
if (_uri.remaining()<=len)
|
||||
{
|
||||
|
@ -459,8 +473,7 @@ public class HttpParser
|
|||
}
|
||||
else if (ch < HttpTokens.SPACE)
|
||||
{
|
||||
badMessage(buffer,HttpStatus.BAD_REQUEST_400,_requestHandler!=null?"No URI":"No Status");
|
||||
return true;
|
||||
throw new BadMessage(HttpStatus.BAD_REQUEST_400,_requestHandler!=null?"No URI":"No Status");
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -581,8 +594,7 @@ public class HttpParser
|
|||
_version=HttpVersion.CACHE.get(takeString());
|
||||
if (_version==null)
|
||||
{
|
||||
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Unknown Version");
|
||||
return true;
|
||||
throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Unknown Version");
|
||||
}
|
||||
|
||||
// Should we try to cache header fields?
|
||||
|
@ -644,8 +656,7 @@ public class HttpParser
|
|||
catch(NumberFormatException e)
|
||||
{
|
||||
LOG.ignore(e);
|
||||
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Bad Content-Length");
|
||||
return true;
|
||||
throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Content-Length");
|
||||
}
|
||||
if (_contentLength <= 0)
|
||||
_endOfContent=EndOfContent.NO_CONTENT;
|
||||
|
@ -663,8 +674,7 @@ public class HttpParser
|
|||
_endOfContent=EndOfContent.CHUNKED_CONTENT;
|
||||
else if (_valueString.indexOf(HttpHeaderValue.CHUNKED.toString()) >= 0)
|
||||
{
|
||||
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Bad chunking");
|
||||
return true;
|
||||
throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad chunking");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -676,8 +686,7 @@ public class HttpParser
|
|||
int port=0;
|
||||
if (host==null || host.length()==0)
|
||||
{
|
||||
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Bad Host header");
|
||||
return true;
|
||||
throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Host header");
|
||||
}
|
||||
|
||||
int len=host.length();
|
||||
|
@ -698,8 +707,7 @@ public class HttpParser
|
|||
catch (NumberFormatException e)
|
||||
{
|
||||
LOG.debug(e);
|
||||
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Bad Host header");
|
||||
return true;
|
||||
throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Host header");
|
||||
}
|
||||
break loop;
|
||||
}
|
||||
|
@ -708,8 +716,7 @@ public class HttpParser
|
|||
{
|
||||
if (host.charAt(len-1)!=']')
|
||||
{
|
||||
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Bad IPv6 Host header");
|
||||
return true;
|
||||
throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad IPv6 Host header");
|
||||
}
|
||||
host = host.substring(1,len-1);
|
||||
}
|
||||
|
@ -772,8 +779,7 @@ public class HttpParser
|
|||
if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
|
||||
{
|
||||
LOG.warn("Header is too large >"+_maxHeaderBytes);
|
||||
badMessage(buffer,HttpStatus.REQUEST_ENTITY_TOO_LARGE_413,null);
|
||||
return true;
|
||||
throw new BadMessage(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
|
||||
}
|
||||
|
||||
switch (_state)
|
||||
|
@ -829,8 +835,7 @@ public class HttpParser
|
|||
// Was there a required host header?
|
||||
if (!_host && _version!=HttpVersion.HTTP_1_0 && _requestHandler!=null)
|
||||
{
|
||||
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"No Host");
|
||||
return true;
|
||||
throw new BadMessage(HttpStatus.BAD_REQUEST_400,"No Host");
|
||||
}
|
||||
|
||||
// is it a response that cannot have a body?
|
||||
|
@ -1321,30 +1326,36 @@ public class HttpParser
|
|||
|
||||
return false;
|
||||
}
|
||||
catch(BadMessage e)
|
||||
{
|
||||
BufferUtil.clear(buffer);
|
||||
|
||||
LOG.warn("badMessage: "+e._code+(e._message!=null?" "+e._message:"")+" for "+_handler);
|
||||
LOG.debug(e);
|
||||
setState(State.CLOSED);
|
||||
_handler.badMessage(e._code, e._message);
|
||||
return false;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
BufferUtil.clear(buffer);
|
||||
if (isClosed())
|
||||
{
|
||||
LOG.debug(e);
|
||||
if (e instanceof IllegalStateException)
|
||||
throw (IllegalStateException)e;
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
LOG.warn("badMessage: "+e.toString()+" for "+_handler);
|
||||
LOG.debug(e);
|
||||
badMessage(buffer,HttpStatus.BAD_REQUEST_400,null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_state.ordinal()<=State.END.ordinal())
|
||||
{
|
||||
setState(State.CLOSED);
|
||||
_handler.badMessage(400,null);
|
||||
}
|
||||
else
|
||||
{
|
||||
_handler.earlyEOF();
|
||||
setState(State.CLOSED);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
private void badMessage(ByteBuffer buffer, int status, String reason)
|
||||
{
|
||||
BufferUtil.clear(buffer);
|
||||
setState(State.CLOSED);
|
||||
_handler.badMessage(status, reason);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,10 +22,12 @@ import java.nio.ByteBuffer;
|
|||
import java.nio.charset.Charset;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jetty.util.ArrayTrie;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
|
@ -194,7 +196,7 @@ public class MimeTypes
|
|||
_mimeMap.put(StringUtil.asciiToLowerCase(ext),normalizeMimeType(mimeMap.get(ext)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get the MIME type by filename extension.
|
||||
* @param filename A file name
|
||||
|
@ -244,6 +246,12 @@ public class MimeTypes
|
|||
_mimeMap.put(StringUtil.asciiToLowerCase(extension),normalizeMimeType(type));
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public static Set<String> getKnownMimeTypes()
|
||||
{
|
||||
return new HashSet<>(__dftMimeMap.values());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private static String normalizeMimeType(String type)
|
||||
{
|
||||
|
|
|
@ -67,7 +67,7 @@ public class MavenAnnotationConfiguration extends AnnotationConfiguration
|
|||
//scan the equivalent of the WEB-INF/classes directory that has been synthesised by the plugin
|
||||
if (f.isDirectory() && f.exists())
|
||||
{
|
||||
doParse(context, parser, Resource.newResource(f.toURL()));
|
||||
doParse(context, parser, Resource.newResource(f.toURI()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,93 +2,10 @@
|
|||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
|
||||
|
||||
<!-- =============================================================== -->
|
||||
<!-- Configure Jetty Plus features -->
|
||||
<!-- -->
|
||||
<!-- This file sets up a WebAppDeployer to automatically deploy all -->
|
||||
<!-- webapps in $jetty.home/webapps-plus at startup time, and to -->
|
||||
<!-- enable all of them with Plus features (jndi etc). -->
|
||||
<!-- -->
|
||||
<!-- You can instead configure individual webapps with Jetty Plus -->
|
||||
<!-- features by using the ContextDeployer (configured in -->
|
||||
<!-- $jetty.home/etc/jetty.xml), and ensuring that you set the -->
|
||||
<!-- same set of classes listed below in the "plusConfig" as the -->
|
||||
<!-- webapp's configurationClasses. -->
|
||||
<!-- -->
|
||||
<!-- For more information about Jetty Plus, see the Jetty wiki at -->
|
||||
<!-- http://docs.codehaus.org/display/JETTY/Jetty+Wiki -->
|
||||
<!-- Configure extended support for webapps -->
|
||||
<!-- =============================================================== -->
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Example JAAS realm setup. -->
|
||||
<!-- The LoginModuleName must be exactly the same as in the -->
|
||||
<!-- login.conf file, and the realm Name must be the same as in -->
|
||||
<!-- the web.xml file. -->
|
||||
<!-- =========================================================== -->
|
||||
<!--
|
||||
<Call name="addBean">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.plus.jaas.JAASLoginService">
|
||||
<Set name="name">xyzrealm</Set>
|
||||
<Set name="LoginModuleName">xyz</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
-->
|
||||
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Enabling plus feature. -->
|
||||
<!-- Choose one of the following methods. NOTE that by default -->
|
||||
<!-- the last method (enabled for all webapps on this Server) is -->
|
||||
<!-- enabled. -->
|
||||
<!-- -->
|
||||
<!-- For a single webapp: -->
|
||||
<!-- Use a context xml file to call -->
|
||||
<!-- setConfigurationClasses(plusConfig). -->
|
||||
<!-- -->
|
||||
<!-- For all webapps in a directory: -->
|
||||
<!-- Uncomment the section entitled "Apply plusConfig to all -->
|
||||
<!-- webapps in webapps-plus". -->
|
||||
<!-- -->
|
||||
<!-- For all webapps deployed via this Server instance: -->
|
||||
<!-- Uncomment the section entitled "Apply plusConfig to all -->
|
||||
<!-- webapps for this Server". NOTE: this is the default. -->
|
||||
<!-- =========================================================== -->
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Apply plusConfig to all webapps in webapps-plus -->
|
||||
<!-- =========================================================== -->
|
||||
<!-- Uncomment the following to set up a deployer that will -->
|
||||
<!-- deploy webapps from a directory called webapps-plus. Note -->
|
||||
<!-- that you will need to create this directory first! -->
|
||||
<!--
|
||||
<Ref refid="DeploymentManager">
|
||||
<Call name="addAppProvider">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
|
||||
<Set name="monitoredDirName"><Property name="jetty.home" default="." />/webapps-plus</Set>
|
||||
<Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>
|
||||
<Set name="scanInterval">5</Set>
|
||||
<Set name="parentLoaderPriority">false</Set>
|
||||
<Set name="extractWars">true</Set>
|
||||
<Set name="configurationClasses">
|
||||
<Array id="plusConfig" type="java.lang.String">
|
||||
<Item>org.eclipse.jetty.webapp.WebInfConfiguration</Item>
|
||||
<Item>org.eclipse.jetty.webapp.WebXmlConfiguration</Item>
|
||||
<Item>org.eclipse.jetty.webapp.MetaInfConfiguration</Item>
|
||||
<Item>org.eclipse.jetty.webapp.FragmentConfiguration</Item>
|
||||
<Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item>
|
||||
<Item>org.eclipse.jetty.plus.webapp.PlusConfiguration</Item>
|
||||
<Item>org.eclipse.jetty.webapp.JettyWebXmlConfiguration</Item>
|
||||
</Array>
|
||||
</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
</Ref>
|
||||
-->
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Add plus Configuring classes to all webapps for this Server -->
|
||||
<!-- =========================================================== -->
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.server;
|
|||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
|
@ -45,6 +46,8 @@ import org.eclipse.jetty.io.EndPoint;
|
|||
import org.eclipse.jetty.io.EofException;
|
||||
import org.eclipse.jetty.server.HttpChannelState.Next;
|
||||
import org.eclipse.jetty.server.handler.ErrorHandler;
|
||||
import org.eclipse.jetty.util.BlockingCallback;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
@ -87,7 +90,8 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
|||
private final HttpURI _uri;
|
||||
private final HttpChannelState _state;
|
||||
private final Request _request;
|
||||
private final Response _response;
|
||||
private final Response _response;
|
||||
private final BlockingCallback _writeblock=new BlockingCallback();
|
||||
private HttpVersion _version = HttpVersion.HTTP_1_1;
|
||||
private boolean _expect = false;
|
||||
private boolean _expect100Continue = false;
|
||||
|
@ -197,7 +201,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
|||
throw new IOException("Committed before 100 Continues");
|
||||
|
||||
// TODO: break this dependency with HttpGenerator
|
||||
boolean committed = commitResponse(HttpGenerator.CONTINUE_100_INFO, null, false);
|
||||
boolean committed = sendResponse(HttpGenerator.CONTINUE_100_INFO, null, false);
|
||||
if (!committed)
|
||||
throw new IOException("Concurrent commit while trying to send 100-Continue");
|
||||
}
|
||||
|
@ -355,7 +359,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
|||
{
|
||||
HttpFields fields = new HttpFields();
|
||||
ResponseInfo info = new ResponseInfo(_request.getHttpVersion(), fields, 0, HttpStatus.INTERNAL_SERVER_ERROR_500, null, _request.isHead());
|
||||
boolean committed = commitResponse(info, null, true);
|
||||
boolean committed = sendResponse(info, null, true);
|
||||
if (!committed)
|
||||
LOG.warn("Could not send response error 500: "+x);
|
||||
}
|
||||
|
@ -499,6 +503,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
|||
if (charset != null)
|
||||
_request.setCharacterEncodingUnchecked(charset);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -583,7 +588,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
|||
{
|
||||
if (_state.handling()==Next.CONTINUE)
|
||||
{
|
||||
commitResponse(new ResponseInfo(HttpVersion.HTTP_1_1,new HttpFields(),0,status,reason,false),null,true);
|
||||
sendResponse(new ResponseInfo(HttpVersion.HTTP_1_1,new HttpFields(),0,status,reason,false),null,true);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
|
@ -596,47 +601,92 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
|||
_state.completed();
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean commitResponse(ResponseInfo info, ByteBuffer content, boolean complete) throws IOException
|
||||
|
||||
protected boolean sendResponse(ResponseInfo info, ByteBuffer content, boolean complete, final Callback callback)
|
||||
{
|
||||
boolean committed = _committed.compareAndSet(false, true);
|
||||
if (committed)
|
||||
boolean committing = _committed.compareAndSet(false, true);
|
||||
if (committing)
|
||||
{
|
||||
try
|
||||
// We need an info to commit
|
||||
if (info==null)
|
||||
{
|
||||
// Try to commit with the passed info
|
||||
_transport.send(info, content, complete);
|
||||
info = _response.newResponseInfo();
|
||||
}
|
||||
|
||||
final int status=info.getStatus();
|
||||
final Callback committed = new Callback()
|
||||
{
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
// If we are committing a 1xx response, we need to reset the commit
|
||||
// status so that the "real" response can be committed again.
|
||||
if (status<200 && status>=100)
|
||||
_committed.set(false);
|
||||
callback.succeeded();
|
||||
}
|
||||
|
||||
// If we are committing a 1xx response, we need to reset the commit
|
||||
// status so that the "real" response can be committed again.
|
||||
if (info.getStatus() < 200)
|
||||
_committed.set(false);
|
||||
}
|
||||
catch (EofException e)
|
||||
{
|
||||
LOG.debug(e);
|
||||
// TODO is it worthwhile sending if we are at EoF?
|
||||
// "application" info failed to commit, commit with a failsafe 500 info
|
||||
_transport.send(HttpGenerator.RESPONSE_500_INFO,null,true);
|
||||
complete=true;
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
// "application" info failed to commit, commit with a failsafe 500 info
|
||||
_transport.send(HttpGenerator.RESPONSE_500_INFO,null,true);
|
||||
complete=true;
|
||||
throw e;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// TODO this indicates the relationship with HttpOutput is not exactly correct
|
||||
if (complete)
|
||||
_response.getHttpOutput().closed();
|
||||
}
|
||||
@Override
|
||||
public void failed(final Throwable x)
|
||||
{
|
||||
if (x instanceof EofException)
|
||||
{
|
||||
LOG.debug(x);
|
||||
_response.getHttpOutput().closed();
|
||||
callback.failed(x);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.warn(x);
|
||||
_transport.send(HttpGenerator.RESPONSE_500_INFO,null,true,new Callback()
|
||||
{
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
_response.getHttpOutput().closed();
|
||||
callback.failed(x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable th)
|
||||
{
|
||||
LOG.ignore(th);
|
||||
_response.getHttpOutput().closed();
|
||||
callback.failed(x);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_transport.send(info, content, complete, committed);
|
||||
}
|
||||
return committed;
|
||||
else if (info==null)
|
||||
{
|
||||
// This is a normal write
|
||||
_transport.send(null, content, complete, callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
callback.failed(new IllegalStateException("committed"));
|
||||
}
|
||||
return committing;
|
||||
}
|
||||
|
||||
protected boolean sendResponse(ResponseInfo info, ByteBuffer content, boolean complete) throws IOException
|
||||
{
|
||||
boolean committing=sendResponse(info,content,complete,_writeblock);
|
||||
|
||||
try
|
||||
{
|
||||
_writeblock.block();
|
||||
}
|
||||
catch (InterruptedException | TimeoutException e)
|
||||
{
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
return committing;
|
||||
}
|
||||
|
||||
protected boolean isCommitted()
|
||||
|
@ -654,17 +704,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
|||
*/
|
||||
protected void write(ByteBuffer content, boolean complete) throws IOException
|
||||
{
|
||||
if (isCommitted())
|
||||
{
|
||||
_transport.send(null, content, complete);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResponseInfo info = _response.newResponseInfo();
|
||||
boolean committed = commitResponse(info, content, complete);
|
||||
if (!committed)
|
||||
throw new IOException("Concurrent commit");
|
||||
}
|
||||
sendResponse(null, content, complete);
|
||||
}
|
||||
|
||||
protected void execute(Runnable task)
|
||||
|
|
|
@ -51,7 +51,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
{
|
||||
public static final String UPGRADE_CONNECTION_ATTRIBUTE = "org.eclipse.jetty.server.HttpConnection.UPGRADE";
|
||||
private static final boolean REQUEST_BUFFER_DIRECT=false;
|
||||
private static final boolean HEADER_BUFFER_DIRECT=true;
|
||||
private static final boolean HEADER_BUFFER_DIRECT=false;
|
||||
private static final boolean CHUNK_BUFFER_DIRECT=false;
|
||||
private static final Logger LOG = Log.getLogger(HttpConnection.class);
|
||||
private static final ThreadLocal<HttpConnection> __currentConnection = new ThreadLocal<>();
|
||||
|
@ -307,149 +307,46 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
|
||||
@Override
|
||||
public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent) throws IOException
|
||||
{
|
||||
// If we are still expecting a 100 continues
|
||||
if (_channel.isExpecting100Continue())
|
||||
// then we can't be persistent
|
||||
_generator.setPersistent(false);
|
||||
|
||||
|
||||
ByteBuffer header = null;
|
||||
ByteBuffer chunk = null;
|
||||
out: while (true)
|
||||
{
|
||||
HttpGenerator.Result result = _generator.generateResponse(info, header, chunk, content, lastContent);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} generate: {} ({},{},{})@{}",
|
||||
this,
|
||||
result,
|
||||
BufferUtil.toSummaryString(header),
|
||||
BufferUtil.toSummaryString(content),
|
||||
lastContent,
|
||||
_generator.getState());
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case NEED_HEADER:
|
||||
{
|
||||
if (lastContent && content!=null && BufferUtil.space(content)>_config.getResponseHeaderSize() && content.hasArray() )
|
||||
{
|
||||
// use spare space in content buffer for header buffer
|
||||
int p=content.position();
|
||||
int l=content.limit();
|
||||
content.position(l);
|
||||
content.limit(l+_config.getResponseHeaderSize());
|
||||
header=content.slice();
|
||||
header.limit(0);
|
||||
content.position(p);
|
||||
content.limit(l);
|
||||
}
|
||||
else
|
||||
header = _bufferPool.acquire(_config.getResponseHeaderSize(), HEADER_BUFFER_DIRECT);
|
||||
continue;
|
||||
}
|
||||
case NEED_CHUNK:
|
||||
{
|
||||
chunk = _chunk;
|
||||
if (chunk==null)
|
||||
chunk = _chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, CHUNK_BUFFER_DIRECT);
|
||||
continue;
|
||||
}
|
||||
case FLUSH:
|
||||
{
|
||||
// Don't write the chunk or the content if this is a HEAD response
|
||||
if (_channel.getRequest().isHead())
|
||||
{
|
||||
BufferUtil.clear(chunk);
|
||||
BufferUtil.clear(content);
|
||||
}
|
||||
|
||||
// If we have a header
|
||||
if (BufferUtil.hasContent(header))
|
||||
{
|
||||
// we know there will not be a chunk, so write either header+content or just the header
|
||||
if (BufferUtil.hasContent(content))
|
||||
blockingWrite(header, content);
|
||||
else
|
||||
blockingWrite(header);
|
||||
|
||||
}
|
||||
else if (BufferUtil.hasContent(chunk))
|
||||
{
|
||||
if (BufferUtil.hasContent(content))
|
||||
blockingWrite(chunk,content);
|
||||
else
|
||||
blockingWrite(chunk);
|
||||
}
|
||||
else if (BufferUtil.hasContent(content))
|
||||
{
|
||||
blockingWrite(content);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case SHUTDOWN_OUT:
|
||||
{
|
||||
getEndPoint().shutdownOutput();
|
||||
continue;
|
||||
}
|
||||
case DONE:
|
||||
{
|
||||
if (header!=null)
|
||||
{
|
||||
// don't release header in spare content buffer
|
||||
if (!lastContent || content==null || !content.hasArray() || !header.hasArray() || content.array()!=header.array())
|
||||
_bufferPool.release(header);
|
||||
}
|
||||
if (chunk!=null)
|
||||
_bufferPool.release(chunk);
|
||||
break out;
|
||||
}
|
||||
case CONTINUE:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new IllegalStateException("generateResponse="+result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback)
|
||||
{
|
||||
try
|
||||
{
|
||||
send(info,content,lastContent);
|
||||
callback.succeeded();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
callback.failed(e);
|
||||
}
|
||||
}
|
||||
// If we are still expecting a 100 continues
|
||||
if (info !=null && _channel.isExpecting100Continue())
|
||||
// then we can't be persistent
|
||||
_generator.setPersistent(false);
|
||||
|
||||
Sender sender = new Sender(content,lastContent,_writeBlocker);
|
||||
sender.process(info);
|
||||
|
||||
private void blockingWrite(ByteBuffer... bytes) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
getEndPoint().write(_writeBlocker, bytes);
|
||||
_writeBlocker.block();
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
x.printStackTrace();
|
||||
throw (IOException)new InterruptedIOException().initCause(x);
|
||||
}
|
||||
catch (TimeoutException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
throw new IOException(e);
|
||||
}
|
||||
catch (ClosedChannelException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
throw new EofException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback)
|
||||
{
|
||||
// If we are still expecting a 100 continues
|
||||
if (info !=null && _channel.isExpecting100Continue())
|
||||
// then we can't be persistent
|
||||
_generator.setPersistent(false);
|
||||
|
||||
new Sender(content,lastContent,callback).process(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completed()
|
||||
|
@ -692,5 +589,144 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
|
||||
}
|
||||
|
||||
private class Sender implements Callback
|
||||
{
|
||||
final ByteBuffer _content;
|
||||
final boolean _lastContent;
|
||||
final Callback _callback;
|
||||
|
||||
Sender(ByteBuffer content, boolean last, Callback callback)
|
||||
{
|
||||
_callback=callback;
|
||||
_content=content;
|
||||
_lastContent=last;
|
||||
}
|
||||
|
||||
public void process(ResponseInfo info)
|
||||
{
|
||||
try
|
||||
{
|
||||
ByteBuffer header = null;
|
||||
ByteBuffer chunk = null;
|
||||
while (true)
|
||||
{
|
||||
HttpGenerator.Result result = _generator.generateResponse(info, header, chunk, _content, _lastContent);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} generate: {} ({},{},{})@{}",
|
||||
this,
|
||||
result,
|
||||
BufferUtil.toSummaryString(header),
|
||||
BufferUtil.toSummaryString(_content),
|
||||
_lastContent,
|
||||
_generator.getState());
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case NEED_HEADER:
|
||||
{
|
||||
if (_lastContent && _content!=null && BufferUtil.space(_content)>_config.getResponseHeaderSize() && _content.hasArray() )
|
||||
{
|
||||
// use spare space in content buffer for header buffer
|
||||
int p=_content.position();
|
||||
int l=_content.limit();
|
||||
_content.position(l);
|
||||
_content.limit(l+_config.getResponseHeaderSize());
|
||||
header=_content.slice();
|
||||
header.limit(0);
|
||||
_content.position(p);
|
||||
_content.limit(l);
|
||||
}
|
||||
else
|
||||
header = _bufferPool.acquire(_config.getResponseHeaderSize(), HEADER_BUFFER_DIRECT);
|
||||
continue;
|
||||
}
|
||||
case NEED_CHUNK:
|
||||
{
|
||||
chunk = _chunk;
|
||||
if (chunk==null)
|
||||
chunk = _chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, CHUNK_BUFFER_DIRECT);
|
||||
continue;
|
||||
}
|
||||
case FLUSH:
|
||||
{
|
||||
// Don't write the chunk or the content if this is a HEAD response
|
||||
if (_channel.getRequest().isHead())
|
||||
{
|
||||
BufferUtil.clear(chunk);
|
||||
BufferUtil.clear(_content);
|
||||
}
|
||||
|
||||
// If we have a header
|
||||
if (BufferUtil.hasContent(header))
|
||||
{
|
||||
// we know there will not be a chunk, so write either header+content or just the header
|
||||
if (BufferUtil.hasContent(_content))
|
||||
getEndPoint().write(this, header, _content);
|
||||
else
|
||||
getEndPoint().write(this, header);
|
||||
}
|
||||
else if (BufferUtil.hasContent(chunk))
|
||||
{
|
||||
if (BufferUtil.hasContent(_content))
|
||||
getEndPoint().write(this, chunk, _content);
|
||||
else
|
||||
getEndPoint().write(this, chunk);
|
||||
}
|
||||
else if (BufferUtil.hasContent(_content))
|
||||
{
|
||||
getEndPoint().write(this, _content);
|
||||
}
|
||||
else
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
case SHUTDOWN_OUT:
|
||||
{
|
||||
getEndPoint().shutdownOutput();
|
||||
continue;
|
||||
}
|
||||
case DONE:
|
||||
{
|
||||
if (header!=null)
|
||||
{
|
||||
// don't release header in spare content buffer
|
||||
if (!_lastContent || _content==null || !_content.hasArray() || !header.hasArray() || _content.array()!=header.array())
|
||||
_bufferPool.release(header);
|
||||
}
|
||||
if (chunk!=null)
|
||||
_bufferPool.release(chunk);
|
||||
_callback.succeeded();
|
||||
return;
|
||||
}
|
||||
case CONTINUE:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new IllegalStateException("generateResponse="+result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
_callback.failed(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
process(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
_callback.failed(x);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -425,7 +425,7 @@ public class Response implements HttpServletResponse
|
|||
{
|
||||
if (_channel.isExpecting102Processing() && !isCommitted())
|
||||
{
|
||||
_channel.commitResponse(HttpGenerator.PROGRESS_102_INFO, null, true);
|
||||
_channel.sendResponse(HttpGenerator.PROGRESS_102_INFO, null, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -158,15 +158,15 @@ public class HttpConnectionTest
|
|||
@Test
|
||||
public void testHead() throws Exception
|
||||
{
|
||||
String responseHEAD=connector.getResponses("HEAD /R1 HTTP/1.1\015\012"+
|
||||
"Host: localhost\015\012"+
|
||||
"Connection: close\015\012"+
|
||||
"\015\012");
|
||||
|
||||
String responsePOST=connector.getResponses("POST /R1 HTTP/1.1\015\012"+
|
||||
"Host: localhost\015\012"+
|
||||
"Connection: close\015\012"+
|
||||
"\015\012");
|
||||
|
||||
String responseHEAD=connector.getResponses("HEAD /R1 HTTP/1.1\015\012"+
|
||||
"Host: localhost\015\012"+
|
||||
"Connection: close\015\012"+
|
||||
"\015\012");
|
||||
|
||||
assertThat(responsePOST,startsWith(responseHEAD.substring(0,responseHEAD.length()-2)));
|
||||
assertThat(responsePOST.length(),greaterThan(responseHEAD.length()));
|
||||
|
|
|
@ -89,6 +89,7 @@ public class ResponseTest
|
|||
@Override
|
||||
public void send(ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback)
|
||||
{
|
||||
callback.succeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -668,13 +668,13 @@ public class DefaultServletTest
|
|||
defholder.setInitParameter("gzip", "true");
|
||||
defholder.setInitParameter("resourceBase", resBasePath);
|
||||
|
||||
String response = connector.getResponses("GET /context/data0.txt HTTP/1.1\r\nHost:localhost:8080\r\n\r\n");
|
||||
String response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\n\r\n");
|
||||
assertResponseContains("Content-Length: 12", response);
|
||||
assertResponseContains("Hello Text 0",response);
|
||||
assertResponseContains("Vary: Accept-Encoding",response);
|
||||
assertResponseNotContains("Content-Encoding: gzip",response);
|
||||
|
||||
response = connector.getResponses("GET /context/data0.txt HTTP/1.1\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\n\r\n");
|
||||
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\n\r\n");
|
||||
assertResponseContains("Content-Length: 9", response);
|
||||
assertResponseContains("fake gzip",response);
|
||||
assertResponseContains("Vary: Accept-Encoding",response);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.eclipse.jetty.servlets;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
|
@ -39,9 +40,11 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.servlets.gzip.AbstractCompressedStream;
|
||||
import org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper;
|
||||
import org.eclipse.jetty.servlets.gzip.GzipOutputStream;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
@ -52,8 +55,8 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* <li>accept-encoding header is set to either gzip, deflate or a combination of those</li>
|
||||
* <li>The response status code is >=200 and <300
|
||||
* <li>The content length is unknown or more than the <code>minGzipSize</code> initParameter or the minGzipSize is 0(default)</li>
|
||||
* <li>The content-type is in the comma separated list of mimeTypes set in the <code>mimeTypes</code> initParameter or
|
||||
* if no mimeTypes are defined the content-type is not "application/gzip"</li>
|
||||
* <li>If a list of mimeTypes is set by the <code>mimeTypes</code> init parameter, then the Content-Type is in the list.</li>
|
||||
* <li>If no mimeType list is set, then the content-type is not in the list defined by <code>excludedMimeTypes</code></li>
|
||||
* <li>No content-encoding is specified by the resource</li>
|
||||
* </ul>
|
||||
*
|
||||
|
@ -71,44 +74,52 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* is set to a comma separated list of user agents, then these agents will be excluded from gzip content.
|
||||
* </p>
|
||||
* <p>Init Parameters:</p>
|
||||
* <PRE>
|
||||
* bufferSize The output buffer size. Defaults to 8192. Be careful as values <= 0 will lead to an
|
||||
* <dl>
|
||||
* <dt>bufferSize</dt> <dd>The output buffer size. Defaults to 8192. Be careful as values <= 0 will lead to an
|
||||
* {@link IllegalArgumentException}.
|
||||
* See: {@link java.util.zip.GZIPOutputStream#GZIPOutputStream(java.io.OutputStream, int)}
|
||||
* and: {@link java.util.zip.DeflaterOutputStream#DeflaterOutputStream(java.io.OutputStream, Deflater, int)}
|
||||
*
|
||||
* minGzipSize Content will only be compressed if content length is either unknown or greater
|
||||
* </dd>
|
||||
* <dt>minGzipSize</dt> <dd>Content will only be compressed if content length is either unknown or greater
|
||||
* than <code>minGzipSize</code>.
|
||||
*
|
||||
* deflateCompressionLevel The compression level used for deflate compression. (0-9).
|
||||
* </dd>
|
||||
* <dt>deflateCompressionLevel</dt> <dd>The compression level used for deflate compression. (0-9).
|
||||
* See: {@link java.util.zip.Deflater#Deflater(int, boolean)}
|
||||
*
|
||||
* deflateNoWrap The noWrap setting for deflate compression. Defaults to true. (true/false)
|
||||
* </dd>
|
||||
* <dt>deflateNoWrap</dt> <dd>The noWrap setting for deflate compression. Defaults to true. (true/false)
|
||||
* See: {@link java.util.zip.Deflater#Deflater(int, boolean)}
|
||||
*
|
||||
* methods Comma separated list of HTTP methods to compress. If not set, only GET requests are compressed.
|
||||
*
|
||||
* mimeTypes Comma separated list of mime types to compress. See description above.
|
||||
*
|
||||
* excludedAgents Comma separated list of user agents to exclude from compression. Does a
|
||||
* </dd>
|
||||
* <dt>methods</dt> <dd>Comma separated list of HTTP methods to compress. If not set, only GET requests are compressed.
|
||||
* </dd>
|
||||
* <dt>mimeTypes</dt> <dd>Comma separated list of mime types to compress. If it is not set, then the excludedMimeTypes list is used.
|
||||
* </dd>
|
||||
* <dt>excludedMimeTypes</dt> <dd>Comma separated list of mime types to never compress. If not set, then the default is the commonly known
|
||||
* image, video, audio and compressed types.
|
||||
* </dd>
|
||||
|
||||
* <dt>excludedAgents</dt> <dd>Comma separated list of user agents to exclude from compression. Does a
|
||||
* {@link String#contains(CharSequence)} to check if the excluded agent occurs
|
||||
* in the user-agent header. If it does -> no compression
|
||||
*
|
||||
* excludeAgentPatterns Same as excludedAgents, but accepts regex patterns for more complex matching.
|
||||
*
|
||||
* excludePaths Comma separated list of paths to exclude from compression.
|
||||
* </dd>
|
||||
* <dt>excludeAgentPatterns</dt> <dd>Same as excludedAgents, but accepts regex patterns for more complex matching.
|
||||
* </dd>
|
||||
* <dt>excludePaths</dt> <dd>Comma separated list of paths to exclude from compression.
|
||||
* Does a {@link String#startsWith(String)} comparison to check if the path matches.
|
||||
* If it does match -> no compression. To match subpaths use <code>excludePathPatterns</code>
|
||||
* instead.
|
||||
*
|
||||
* excludePathPatterns Same as excludePath, but accepts regex patterns for more complex matching.
|
||||
*
|
||||
* vary Set to the value of the Vary header sent with responses that could be compressed. By default it is
|
||||
* </dd>
|
||||
* <dt>excludePathPatterns</dt> <dd>Same as excludePath, but accepts regex patterns for more complex matching.
|
||||
* </dd>
|
||||
* <dt>vary</dt> <dd>Set to the value of the Vary header sent with responses that could be compressed. By default it is
|
||||
* set to 'Vary: Accept-Encoding, User-Agent' since IE6 is excluded by default from the excludedAgents.
|
||||
* If user-agents are not to be excluded, then this can be set to 'Vary: Accept-Encoding'. Note also
|
||||
* that shared caches may cache copies of a resource that is varied by User-Agent - one per variation of
|
||||
* the User-Agent, unless the cache does some normalization of the UA string.
|
||||
* </PRE>
|
||||
* </dd>
|
||||
* <dt>checkGzExists</dt> <dd>If set to true, the filter check if a static resource with ".gz" appended exists. If so then
|
||||
* the normal processing is done so that the default servlet can send the pre existing gz content.
|
||||
* </dd>
|
||||
* </dl>
|
||||
*/
|
||||
public class GzipFilter extends UserAgentFilter
|
||||
{
|
||||
|
@ -120,11 +131,13 @@ public class GzipFilter extends UserAgentFilter
|
|||
public final static String ETAG="o.e.j.s.GzipFilter.ETag";
|
||||
|
||||
protected ServletContext _context;
|
||||
protected Set<String> _mimeTypes;
|
||||
protected final Set<String> _mimeTypes=new HashSet<>();
|
||||
protected boolean _excludeMimeTypes;
|
||||
protected int _bufferSize=8192;
|
||||
protected int _minGzipSize=256;
|
||||
protected int _deflateCompressionLevel=Deflater.DEFAULT_COMPRESSION;
|
||||
protected boolean _deflateNoWrap = true;
|
||||
protected boolean _checkGzExists = true;
|
||||
|
||||
// non-static, as other GzipFilter instances may have different configurations
|
||||
protected final ThreadLocal<Deflater> _deflater = new ThreadLocal<Deflater>();
|
||||
|
@ -169,6 +182,10 @@ public class GzipFilter extends UserAgentFilter
|
|||
if (tmp!=null)
|
||||
_deflateNoWrap=Boolean.parseBoolean(tmp);
|
||||
|
||||
tmp=filterConfig.getInitParameter("checkGzExists");
|
||||
if (tmp!=null)
|
||||
_checkGzExists=Boolean.parseBoolean(tmp);
|
||||
|
||||
tmp=filterConfig.getInitParameter("methods");
|
||||
if (tmp!=null)
|
||||
{
|
||||
|
@ -180,9 +197,32 @@ public class GzipFilter extends UserAgentFilter
|
|||
_methods.add(HttpMethod.GET.asString());
|
||||
|
||||
tmp=filterConfig.getInitParameter("mimeTypes");
|
||||
if (tmp!=null)
|
||||
if (tmp==null)
|
||||
{
|
||||
_excludeMimeTypes=true;
|
||||
tmp=filterConfig.getInitParameter("excludedMimeTypes");
|
||||
if (tmp==null)
|
||||
{
|
||||
for (String type:MimeTypes.getKnownMimeTypes())
|
||||
{
|
||||
if (type.startsWith("image/")||
|
||||
type.startsWith("audio/")||
|
||||
type.startsWith("video/"))
|
||||
_mimeTypes.add(type);
|
||||
_mimeTypes.add("application/compress");
|
||||
_mimeTypes.add("application/zip");
|
||||
_mimeTypes.add("application/gzip");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
StringTokenizer tok = new StringTokenizer(tmp,",",false);
|
||||
while (tok.hasMoreTokens())
|
||||
_mimeTypes.add(tok.nextToken());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_mimeTypes=new HashSet<String>();
|
||||
StringTokenizer tok = new StringTokenizer(tmp,",",false);
|
||||
while (tok.hasMoreTokens())
|
||||
_mimeTypes.add(tok.nextToken());
|
||||
|
@ -196,7 +236,7 @@ public class GzipFilter extends UserAgentFilter
|
|||
_excludedAgents.add(tok.nextToken());
|
||||
}
|
||||
|
||||
tmp=filterConfig.getInitParameter("excludeAgentPatterns");
|
||||
tmp=filterConfig.getInitParameter("excludeAgentPatterns");
|
||||
if (tmp!=null)
|
||||
{
|
||||
_excludedAgentPatterns=new HashSet<Pattern>();
|
||||
|
@ -257,17 +297,32 @@ public class GzipFilter extends UserAgentFilter
|
|||
}
|
||||
|
||||
// Exclude non compressible mime-types known from URI extension. - no Vary because no matter what client, this URI is always excluded
|
||||
if (_mimeTypes!=null && _mimeTypes.size()>0)
|
||||
if (_mimeTypes.size()>0)
|
||||
{
|
||||
String mimeType = _context.getMimeType(request.getRequestURI());
|
||||
|
||||
if (mimeType!=null && !_mimeTypes.contains(mimeType))
|
||||
if (mimeType!=null && _mimeTypes.contains(mimeType)==_excludeMimeTypes)
|
||||
{
|
||||
// handle normally without setting vary header
|
||||
super.doFilter(request,response,chain);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_checkGzExists && request.getServletContext()!=null)
|
||||
{
|
||||
String path=request.getServletContext().getRealPath(URIUtil.addPaths(request.getServletPath(),request.getPathInfo()));
|
||||
if (path!=null)
|
||||
{
|
||||
File gz=new File(path+".gz");
|
||||
if (gz.exists())
|
||||
{
|
||||
// allow default servlet to handle
|
||||
super.doFilter(request,response,chain);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Excluded User-Agents
|
||||
String ua = getUserAgent(request);
|
||||
|
@ -457,7 +512,7 @@ public class GzipFilter extends UserAgentFilter
|
|||
|
||||
protected void configureWrappedResponse(CompressedResponseWrapper wrappedResponse)
|
||||
{
|
||||
wrappedResponse.setMimeTypes(_mimeTypes);
|
||||
wrappedResponse.setMimeTypes(_mimeTypes,_excludeMimeTypes);
|
||||
wrappedResponse.setBufferSize(_bufferSize);
|
||||
wrappedResponse.setMinCompressSize(_minGzipSize);
|
||||
}
|
||||
|
|
|
@ -27,12 +27,14 @@ import java.util.zip.Deflater;
|
|||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.io.UncheckedPrintWriter;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.servlets.gzip.AbstractCompressedStream;
|
||||
import org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper;
|
||||
|
||||
|
@ -144,10 +146,16 @@ public class IncludableGzipFilter extends GzipFilter
|
|||
@Override
|
||||
public void setHeader(String name,String value)
|
||||
{
|
||||
super.setHeader(name,value);
|
||||
HttpServletResponse response = (HttpServletResponse)getResponse();
|
||||
if (!response.containsHeader(name))
|
||||
response.setHeader("org.eclipse.jetty.server.include."+name,value);
|
||||
if (getRequest().getDispatcherType()==DispatcherType.INCLUDE)
|
||||
{
|
||||
if (!"etag".equalsIgnoreCase(name) && !name.startsWith("content-"))
|
||||
{
|
||||
HttpServletResponse response = (HttpServletResponse)getResponse();
|
||||
response.setHeader("org.eclipse.jetty.server.include."+name,value);
|
||||
}
|
||||
}
|
||||
else
|
||||
super.setHeader(name,value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -42,6 +42,7 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
|
|||
public static final int DEFAULT_MIN_COMPRESS_SIZE = 256;
|
||||
|
||||
private Set<String> _mimeTypes;
|
||||
private boolean _excludeMimeTypes;
|
||||
private int _bufferSize=DEFAULT_BUFFER_SIZE;
|
||||
private int _minCompressSize=DEFAULT_MIN_COMPRESS_SIZE;
|
||||
protected HttpServletRequest _request;
|
||||
|
@ -67,6 +68,7 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public int getBufferSize()
|
||||
{
|
||||
return _bufferSize;
|
||||
|
@ -92,16 +94,15 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setMimeTypes(java.util.Set)
|
||||
*/
|
||||
public void setMimeTypes(Set<String> mimeTypes)
|
||||
public void setMimeTypes(Set<String> mimeTypes,boolean excludeMimeTypes)
|
||||
{
|
||||
_excludeMimeTypes=excludeMimeTypes;
|
||||
_mimeTypes = mimeTypes;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setBufferSize(int)
|
||||
*/
|
||||
@Override
|
||||
public void setBufferSize(int bufferSize)
|
||||
|
@ -127,18 +128,17 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
|
|||
{
|
||||
super.setContentType(ct);
|
||||
|
||||
if (ct!=null)
|
||||
if (_compressedStream==null || _compressedStream.getOutputStream()==null)
|
||||
{
|
||||
int colon=ct.indexOf(";");
|
||||
if (colon>0)
|
||||
ct=ct.substring(0,colon);
|
||||
}
|
||||
if (ct!=null)
|
||||
{
|
||||
int colon=ct.indexOf(";");
|
||||
if (colon>0)
|
||||
ct=ct.substring(0,colon);
|
||||
|
||||
if ((_compressedStream==null || _compressedStream.getOutputStream()==null) &&
|
||||
(_mimeTypes==null && ct!=null && ct.contains("gzip") ||
|
||||
_mimeTypes!=null && (ct==null||!_mimeTypes.contains(StringUtil.asciiToLowerCase(ct)))))
|
||||
{
|
||||
noCompression();
|
||||
if (_mimeTypes.contains(StringUtil.asciiToLowerCase(ct))==_excludeMimeTypes)
|
||||
noCompression();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,6 +146,7 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
|
|||
/**
|
||||
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setStatus(int, java.lang.String)
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void setStatus(int sc, String sm)
|
||||
{
|
||||
|
|
|
@ -62,8 +62,9 @@ public class GzipHandler extends HandlerWrapper
|
|||
{
|
||||
private static final Logger LOG = Log.getLogger(GzipHandler.class);
|
||||
|
||||
protected Set<String> _mimeTypes;
|
||||
protected Set<String> _excluded;
|
||||
final protected Set<String> _mimeTypes=new HashSet<>();
|
||||
protected boolean _excludeMimeTypes=false;
|
||||
protected Set<String> _excludedUA;
|
||||
protected int _bufferSize = 8192;
|
||||
protected int _minGzipSize = 256;
|
||||
protected String _vary = "Accept-Encoding, User-Agent";
|
||||
|
@ -96,7 +97,9 @@ public class GzipHandler extends HandlerWrapper
|
|||
*/
|
||||
public void setMimeTypes(Set<String> mimeTypes)
|
||||
{
|
||||
_mimeTypes = mimeTypes;
|
||||
_excludeMimeTypes=false;
|
||||
_mimeTypes.clear();
|
||||
_mimeTypes.addAll(mimeTypes);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -110,7 +113,8 @@ public class GzipHandler extends HandlerWrapper
|
|||
{
|
||||
if (mimeTypes != null)
|
||||
{
|
||||
_mimeTypes = new HashSet<String>();
|
||||
_excludeMimeTypes=false;
|
||||
_mimeTypes.clear();
|
||||
StringTokenizer tok = new StringTokenizer(mimeTypes,",",false);
|
||||
while (tok.hasMoreTokens())
|
||||
{
|
||||
|
@ -119,6 +123,15 @@ public class GzipHandler extends HandlerWrapper
|
|||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Set the mime types.
|
||||
*/
|
||||
public void setExcludeMimeTypes(boolean exclude)
|
||||
{
|
||||
_excludeMimeTypes=exclude;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Get the excluded user agents.
|
||||
|
@ -127,7 +140,7 @@ public class GzipHandler extends HandlerWrapper
|
|||
*/
|
||||
public Set<String> getExcluded()
|
||||
{
|
||||
return _excluded;
|
||||
return _excludedUA;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -139,7 +152,7 @@ public class GzipHandler extends HandlerWrapper
|
|||
*/
|
||||
public void setExcluded(Set<String> excluded)
|
||||
{
|
||||
_excluded = excluded;
|
||||
_excludedUA = excluded;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -153,10 +166,10 @@ public class GzipHandler extends HandlerWrapper
|
|||
{
|
||||
if (excluded != null)
|
||||
{
|
||||
_excluded = new HashSet<String>();
|
||||
_excludedUA = new HashSet<String>();
|
||||
StringTokenizer tok = new StringTokenizer(excluded,",",false);
|
||||
while (tok.hasMoreTokens())
|
||||
_excluded.add(tok.nextToken());
|
||||
_excludedUA.add(tok.nextToken());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,10 +257,10 @@ public class GzipHandler extends HandlerWrapper
|
|||
if (ae != null && ae.indexOf("gzip")>=0 && !response.containsHeader("Content-Encoding")
|
||||
&& !HttpMethod.HEAD.is(request.getMethod()))
|
||||
{
|
||||
if (_excluded!=null)
|
||||
if (_excludedUA!=null)
|
||||
{
|
||||
String ua = request.getHeader("User-Agent");
|
||||
if (_excluded.contains(ua))
|
||||
if (_excludedUA.contains(ua))
|
||||
{
|
||||
_handler.handle(target,baseRequest, request, response);
|
||||
return;
|
||||
|
@ -326,7 +339,7 @@ public class GzipHandler extends HandlerWrapper
|
|||
return new CompressedResponseWrapper(request,response)
|
||||
{
|
||||
{
|
||||
super.setMimeTypes(GzipHandler.this._mimeTypes);
|
||||
super.setMimeTypes(GzipHandler.this._mimeTypes,GzipHandler.this._excludeMimeTypes);
|
||||
super.setBufferSize(GzipHandler.this._bufferSize);
|
||||
super.setMinCompressSize(GzipHandler.this._minGzipSize);
|
||||
}
|
||||
|
|
|
@ -334,7 +334,7 @@ public class EventSourceServletTest
|
|||
{
|
||||
// Read and discard the HTTP response
|
||||
InputStream input = socket.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
|
||||
String line = reader.readLine();
|
||||
while (line != null)
|
||||
{
|
||||
|
|
|
@ -47,10 +47,6 @@ public class PipelineHelper
|
|||
|
||||
public PipelineHelper(URI uri, String encodingHeader)
|
||||
{
|
||||
if (LOG instanceof StdErrLog)
|
||||
{
|
||||
((StdErrLog)LOG).setLevel(StdErrLog.LEVEL_DEBUG);
|
||||
}
|
||||
this.uri = uri;
|
||||
this.endpoint = new InetSocketAddress(uri.getHost(),uri.getPort());
|
||||
this.encodingHeader = encodingHeader;
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.websocket.helper.CaptureSocket;
|
||||
import org.eclipse.jetty.websocket.helper.SafariD00;
|
||||
import org.eclipse.jetty.websocket.helper.WebSocketCaptureServlet;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class WebSocketMinVersionTest
|
||||
{
|
||||
private Server server;
|
||||
private WebSocketCaptureServlet servlet;
|
||||
private URI serverUri;
|
||||
|
||||
@BeforeClass
|
||||
public static void initLogging()
|
||||
{
|
||||
// Configure Logging
|
||||
// System.setProperty("org.eclipse.jetty.util.log.class",StdErrLog.class.getName());
|
||||
System.setProperty("org.eclipse.jetty.websocket.helper.LEVEL","DEBUG");
|
||||
}
|
||||
|
||||
@Before
|
||||
public void startServer() throws Exception
|
||||
{
|
||||
// Configure Server
|
||||
server = new Server(0);
|
||||
|
||||
ServletContextHandler context = new ServletContextHandler();
|
||||
context.setContextPath("/");
|
||||
server.setHandler(context);
|
||||
|
||||
// Serve capture servlet
|
||||
servlet = new WebSocketCaptureServlet();
|
||||
ServletHolder holder = new ServletHolder(servlet);
|
||||
holder.setInitParameter("minVersion","8");
|
||||
context.addServlet(holder,"/");
|
||||
|
||||
// Start Server
|
||||
server.start();
|
||||
|
||||
Connector conn = server.getConnectors()[0];
|
||||
String host = conn.getHost();
|
||||
if (host == null)
|
||||
{
|
||||
host = "localhost";
|
||||
}
|
||||
int port = conn.getLocalPort();
|
||||
serverUri = new URI(String.format("ws://%s:%d/",host,port));
|
||||
// System.out.printf("Server URI: %s%n",serverUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAttemptUpgrade() throws Exception
|
||||
{
|
||||
SafariD00 safari = new SafariD00(serverUri);
|
||||
|
||||
try
|
||||
{
|
||||
safari.connect();
|
||||
safari.issueHandshake();
|
||||
Assert.fail("Expected upgrade failure");
|
||||
}
|
||||
catch(IllegalStateException e) {
|
||||
String respHeader = e.getMessage();
|
||||
Assert.assertThat("Response Header", respHeader, containsString("HTTP/1.1 400 Unsupported websocket version specification"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
// System.out.println("Closing client socket");
|
||||
safari.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public static void threadSleep(int dur, TimeUnit unit) throws InterruptedException
|
||||
{
|
||||
long ms = TimeUnit.MILLISECONDS.convert(dur,unit);
|
||||
Thread.sleep(ms);
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue