Merge remote-tracking branch 'origin/master' into jetty-9.1

Conflicts:
	aggregates/jetty-all/pom.xml
	examples/async-rest/async-rest-jar/pom.xml
	examples/async-rest/async-rest-webapp/pom.xml
	examples/async-rest/pom.xml
	examples/embedded/pom.xml
	examples/pom.xml
	jetty-annotations/pom.xml
	jetty-ant/pom.xml
	jetty-client/pom.xml
	jetty-continuation/pom.xml
	jetty-deploy/pom.xml
	jetty-distribution/pom.xml
	jetty-http/pom.xml
	jetty-io/pom.xml
	jetty-jaas/pom.xml
	jetty-jaspi/pom.xml
	jetty-jmx/pom.xml
	jetty-jndi/pom.xml
	jetty-jsp/pom.xml
	jetty-jspc-maven-plugin/pom.xml
	jetty-maven-plugin/pom.xml
	jetty-monitor/pom.xml
	jetty-nosql/pom.xml
	jetty-osgi/jetty-osgi-boot-jsp/pom.xml
	jetty-osgi/jetty-osgi-boot-warurl/pom.xml
	jetty-osgi/jetty-osgi-boot/pom.xml
	jetty-osgi/jetty-osgi-httpservice/pom.xml
	jetty-osgi/jetty-osgi-npn/pom.xml
	jetty-osgi/pom.xml
	jetty-osgi/test-jetty-osgi-context/pom.xml
	jetty-osgi/test-jetty-osgi-webapp/pom.xml
	jetty-plus/pom.xml
	jetty-proxy/pom.xml
	jetty-rewrite/pom.xml
	jetty-runner/pom.xml
	jetty-security/pom.xml
	jetty-server/pom.xml
	jetty-servlet/pom.xml
	jetty-servlets/pom.xml
	jetty-spdy/pom.xml
	jetty-spdy/spdy-client/pom.xml
	jetty-spdy/spdy-core/pom.xml
	jetty-spdy/spdy-example-webapp/pom.xml
	jetty-spdy/spdy-http-server/pom.xml
	jetty-spdy/spdy-server/pom.xml
	jetty-spring/pom.xml
	jetty-start/pom.xml
	jetty-util-ajax/pom.xml
	jetty-util/pom.xml
	jetty-webapp/pom.xml
	jetty-websocket/pom.xml
	jetty-websocket/websocket-api/pom.xml
	jetty-websocket/websocket-client/pom.xml
	jetty-websocket/websocket-common/pom.xml
	jetty-websocket/websocket-server/pom.xml
	jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/Fuzzer.java
	jetty-websocket/websocket-servlet/pom.xml
	jetty-xml/pom.xml
	pom.xml
	tests/pom.xml
	tests/test-continuation/pom.xml
	tests/test-loginservice/pom.xml
	tests/test-sessions/pom.xml
	tests/test-sessions/test-hash-sessions/pom.xml
	tests/test-sessions/test-jdbc-sessions/pom.xml
	tests/test-sessions/test-sessions-common/pom.xml
	tests/test-webapps/pom.xml
	tests/test-webapps/test-jaas-webapp/pom.xml
	tests/test-webapps/test-jetty-webapp/pom.xml
	tests/test-webapps/test-jndi-webapp/pom.xml
	tests/test-webapps/test-mock-resources/pom.xml
	tests/test-webapps/test-proxy-webapp/pom.xml
	tests/test-webapps/test-servlet-spec/pom.xml
	tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml
	tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml
	tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml
	tests/test-webapps/test-webapp-rfc2616/pom.xml
This commit is contained in:
Greg Wilkins 2013-06-27 09:12:28 +10:00
commit e4716e05e1
33 changed files with 772 additions and 339 deletions

View File

@ -1,6 +1,141 @@
jetty-9.0.4-SNAPSHOT
jetty-9.1.0-SNAPSHOT
jetty-9.0.3.v20130506 06 May 2013
jetty-9.0.4.v20130625 - 25 June 2013
+ 396706 CGI support parameters
+ 397051 Make JDBCLoginService data members protected to facilitate
subclassing
+ 397193 MongoSessionManager refresh updates last access time
+ 398467 Servlet 3.1 Non Blocking IO
+ 400503 WebSocket - squelch legitimate Exceptions during testing to avoid
false positives
+ 401027 javadoc JMX annotations
+ 404508 enable overlay deployer
+ 405188 HTTP 1.0 with GET returns internal IP address.
+ 405313 Websocket client SSL hostname verification is broken, always defaults
to raw IP as String
+ 406759 supressed stacktrace in ReferrerPushStrategyTest
+ 406923 Accept CRLF or LF but not CR as line termination
+ 407246 Test harness checked results in callbacks ignored.
+ 407325 Test Failure:
org.eclipse.jetty.servlets.EventSourceServletTest.testEncoding
+ 407326 Test Failure:
org.eclipse.jetty.client.HttpClientStreamTest.testInputStreamResponseListenerFailedBeforeResponse[0].
+ 407342 ReloadedSessionMissingClassTest uses class compiled with jdk7
+ 407386 Cookies not copied in ServletWebSocketRequest
+ 407469 Method parameters for @OnWebSocketError should support Throwable
+ 407470 Javadoc for @OnWebSocketFrame incorrectly references WebSocketFrame
object
+ 407491 Better handle empty Accept-Language
+ 407614 added excludedMimeTypes to gzipFilter
+ 407812 jetty-maven-plugin can not handle whitespaces in equivalent of
WEB-INF/classes paths
+ 407931 Add toggle for failing on servlet availability
+ 407976 JDBCSessionIdManager potentially leaves server in bad state after
startup
+ 408077 HashSessionManager leaves file handles open after being stopped
+ 408117 isAsyncStarted is false on redispatch
+ 408118 NullPointerException when parsing request cookies
+ 408167 JDBCSessionManager don't mark session as dirty if same attribute
value set
+ 408281 Inconsistent start/stop handling in ContainerLifeCycle
+ 408446 Multipart parsing issue with boundry and charset in ContentType
header
+ 408529 Etags set in 304 response
+ 408600 set correct jetty.url in all pom files
+ 408642 setContentType from addHeader
+ 408662 In pax-web servlet services requests even if init() has not finished
running
+ 408709 refactor test-webapp's chat application. Now there's only a single
request for user login and initial chat message.
+ 408720 NPE in AsyncContext.getRequest()
+ 408723 Jetty Maven plugin reload ignores web.xml listeners
+ 408768 JSTL jars not scanned by jetty-ant
+ 408771 Problem with ShutdownMonitor for jetty-ant
+ 408782 Transparent Proxy - rewrite URL is ignoring query strings.
+ 408806 getParameter returns null on Multipart request if called before
request.getPart()/getParts()
+ 408904 Enhance CommandlineBuilder to not escape strings inside single quotes
+ 408909 GzipFilter setting of headers when reset and/or not compressed
+ 408910 META-INF/jetty-webapp-context.xml file should be able to refer to
bundle-relative locations
+ 408923 Need to be able to configure the ThreadPool for the default jetty
server in osgi
+ 408945 XML Args ignored without DTD
+ 409012 added reference to example rewrite rules
+ 409133 Empty <welcome-file> causes StackOverflowError
+ 409228 Set jetty.home property so config files work even if deployed inside
a bundle
+ 409403 fix IllegalStateException when SPDY is used and the response is
written through BufferUtil.writeTo byte by byte
+ 409436 NPE on context restart using dynamic servlet registration
+ 409441 jetty.xml threadpool arg injection
+ 409449 Ensure servlets, filters and listeners added via dynamic
registration, annotations or descriptors are cleaned on context restarts
+ 409545 Change HttpChannel contract
+ 409556 Resource files not closed
+ 409598 spdy: Fix NPE when a broken client tried to create duplicate stream
IDs
+ 409684 Ids and properties not set for execution of jetty xml config files
with mvn plugin
+ 409796 fix intermittent test issue in
ReferrerPushStrategy.testResourceOrder. Happened when the client got closed
before the server finished sending all data frames. Client waits now until
all data is received.
+ 409801 Jetty should allow webdefault to be specified using a relative
location when running in OSGi
+ 409842 Suspended request completed by a request thread does not set read
interest.
+ 409953 return buffer.slice() instead of buffer.asReadOnlyBuffer() in
ResourceCache to avoid using inefficent path in BufferUtil.writeTo
+ 409978 Websocket shouldn't create HttpSession if not present
+ 410083 Jetty clients submits incomplete URL to proxy.
+ 410098 inject accept-encoding header for all http requests through SPDY as
SPDY clients MUST support spdy. Also remove two new tests that have been to
implementation agnostic and not needed anymore due to recent code changes
+ 410175 WebSocketSession#isSecure() doesn't return true for SSL session on
the server side
+ 410246 HttpClient with proxy does not tunnel HTTPS requests.
+ 410337 throw EofException instead of EOFException in HttpOutput.write() if
HttpOutpyt is closed
+ 410341 suppress stacktraces that happen during test setup shutdown after
successful test run
+ 410370 WebSocketCreator.createWebSocket() should use servlet specific
parameters
+ 410372 Make SSL client certificate information available to server
websockets
+ 410386 WebSocket Session.getUpgradeRequest().getRequestURI() returns bad URI
on server side
+ 410405 Avoid NPE for requestDispatcher(../)
+ 410469 UpgradeRequest is sent twice when using SSL, one fails warning about
WritePendingException
+ 410498 ignore type of exception in GoAwayTest.testDataNotProcessedAfterGoAway
+ 410522 jetty start broken for command line options
+ 410537 Exceptions during @OnWebSocketConnect not reported to
@OnWebSocketError
+ 410559 Removed FillInterest race
+ 410630 MongoSessionManager conflicting session update op
+ 410693 ServletContextHandler.setHandler does not relink handlers - check for
null
+ 410750 NoSQLSessions: implement session context data persistence across
server restarts
+ 410799 errors while creating push streams in HttpTransportOverSPDY are now
logged to debug instead of warn
+ 410893 async support defaults to false for spec created servlets and filters
+ 410911 Continuation isExpired handling.
+ 410995 Avoid reverse DNS lookups when creating SSLEngines.
+ 411061 fix cookie handling in spdy. If two different HTTP headers with the
same name are set, they should be translated to a single multiheader value
according to:
http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3#TOC-2.6.10-Name-Value-Header-Block.
That applies for Set-Cookie headers for example. Before this changed
duplicate header names have overwritten the previous one
+ 411135 HttpClient may send proxied https requests to the proxy instead of
the target server.
+ 411340 add comment why executeOnFillable defaults to true
+ 411545 SslConnection.DecryptedEndpoint.fill() sometimes misses a few network
bytes
jetty-9.0.3.v20130506 - 06 May 2013
+ 404010 fix cast exception in mongodb session manager
+ 404911 WebSocketCloseTest fails spuriously
+ 405281 allow filemappedbuffers to not be used

View File

@ -43,12 +43,12 @@
<td>
<h2>information ...</h2>
<ul>
<li><a href="http://www.eclipse.org/jetty/">Jetty @ Eclipse Home</a></li>
<li><a href="http://wiki.eclipse.org/Jetty">Jetty @ Eclipse Doco</a></li>
<li><a href="http://www.eclipse.org/jetty/">Jetty Homepage</a></li>
<li><a href="http://www.eclipse.org/jetty/documentation/current">Jetty Documentation</a></li>
<li><a href="/proxy/apidocs/">Javadoc</a> (via transparent proxy)</li>
<li><a href="/proxy/xref/">Xref</a> (via transparent proxy)</li>
<li><a
href="http://docs.codehaus.org/display/JETTY/Jetty+Powered">Jetty Powered</a></li>
href="http://www.eclipse.org/jetty/powered">Jetty Powered</a></li>
</ul>
</td>
<td>

View File

@ -55,13 +55,18 @@ public class HttpGenerator
private long _contentPrepared = 0;
private boolean _noContent = false;
private Boolean _persistent = null;
private boolean _sendServerVersion;
private final int _send;
private final static int SEND_SERVER=0x01;
private final static int SEND_XPOWEREDBY=0x02;
/* ------------------------------------------------------------------------------- */
public static void setServerVersion(String version)
{
SERVER=StringUtil.getBytes("Server: Jetty("+version+")\015\012");
SEND[SEND_SERVER]=StringUtil.getBytes("Server: Jetty("+version+")\015\012");
SEND[SEND_XPOWEREDBY]=StringUtil.getBytes("X-Powered-By: Jetty("+version+")\015\012");
SEND[SEND_SERVER|SEND_XPOWEREDBY]=StringUtil.getBytes("Server: Jetty("+version+")\015\012X-Powered-By: Jetty("+version+")\015\012");
}
/* ------------------------------------------------------------------------------- */
@ -71,6 +76,13 @@ public class HttpGenerator
/* ------------------------------------------------------------------------------- */
public HttpGenerator()
{
this(false,false);
}
/* ------------------------------------------------------------------------------- */
public HttpGenerator(boolean sendServerVersion,boolean sendXPoweredBy)
{
_send=(sendServerVersion?SEND_SERVER:0) | (sendXPoweredBy?SEND_XPOWEREDBY:0);
}
/* ------------------------------------------------------------------------------- */
@ -86,15 +98,17 @@ public class HttpGenerator
}
/* ------------------------------------------------------------ */
@Deprecated
public boolean getSendServerVersion ()
{
return _sendServerVersion;
return (_send&SEND_SERVER)!=0;
}
/* ------------------------------------------------------------ */
@Deprecated
public void setSendServerVersion (boolean sendServerVersion)
{
_sendServerVersion = sendServerVersion;
throw new UnsupportedOperationException();
}
/* ------------------------------------------------------------ */
@ -537,11 +551,11 @@ public class HttpGenerator
/* ------------------------------------------------------------ */
private void generateHeaders(Info _info,ByteBuffer header,ByteBuffer content,boolean last)
{
final RequestInfo _request=(_info instanceof RequestInfo)?(RequestInfo)_info:null;
final ResponseInfo _response=(_info instanceof ResponseInfo)?(ResponseInfo)_info:null;
final RequestInfo request=(_info instanceof RequestInfo)?(RequestInfo)_info:null;
final ResponseInfo response=(_info instanceof ResponseInfo)?(ResponseInfo)_info:null;
// default field values
boolean has_server = false;
int send=_send;
HttpField transfer_encoding=null;
boolean keep_alive=false;
boolean close=false;
@ -584,7 +598,7 @@ public class HttpGenerator
case CONNECTION:
{
if (_request!=null)
if (request!=null)
field.putTo(header);
// Lookup and/or split connection value field
@ -619,7 +633,7 @@ public class HttpGenerator
case CLOSE:
{
close=true;
if (_response!=null)
if (response!=null)
{
_persistent=false;
if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
@ -633,7 +647,7 @@ public class HttpGenerator
if (_info.getHttpVersion() == HttpVersion.HTTP_1_0)
{
keep_alive = true;
if (_response!=null)
if (response!=null)
_persistent=true;
}
break;
@ -656,11 +670,8 @@ public class HttpGenerator
case SERVER:
{
if (getSendServerVersion())
{
has_server=true;
field.putTo(header);
}
send=send&~SEND_SERVER;
field.putTo(header);
break;
}
@ -680,7 +691,7 @@ public class HttpGenerator
// 4. Content-Length
// 5. multipart/byteranges
// 6. close
int status=_response!=null?_response.getStatus():-1;
int status=response!=null?response.getStatus():-1;
switch (_endOfContent)
{
case UNKNOWN_CONTENT:
@ -688,14 +699,14 @@ public class HttpGenerator
// written yet?
// Response known not to have a body
if (_contentPrepared == 0 && _response!=null && (status < 200 || status == 204 || status == 304))
if (_contentPrepared == 0 && response!=null && (status < 200 || status == 204 || status == 304))
_endOfContent=EndOfContent.NO_CONTENT;
else if (_info.getContentLength()>0)
{
// we have been given a content length
_endOfContent=EndOfContent.CONTENT_LENGTH;
long content_length = _info.getContentLength();
if ((_response!=null || content_length>0 || content_type ) && !_noContent)
if ((response!=null || content_length>0 || content_type ) && !_noContent)
{
// known length but not actually set.
header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
@ -710,7 +721,7 @@ public class HttpGenerator
long content_length = _contentPrepared+BufferUtil.length(content);
// Do we need to tell the headers about it
if ((_response!=null || content_length>0 || content_type ) && !_noContent)
if ((response!=null || content_length>0 || content_type ) && !_noContent)
{
header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
BufferUtil.putDecLong(header, content_length);
@ -721,7 +732,7 @@ public class HttpGenerator
{
// No idea, so we must assume that a body is coming
_endOfContent = (!isPersistent() || _info.getHttpVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal() ) ? EndOfContent.EOF_CONTENT : EndOfContent.CHUNKED_CONTENT;
if (_response!=null && _endOfContent==EndOfContent.EOF_CONTENT)
if (response!=null && _endOfContent==EndOfContent.EOF_CONTENT)
{
_endOfContent=EndOfContent.NO_CONTENT;
_noContent=true;
@ -731,7 +742,7 @@ public class HttpGenerator
case CONTENT_LENGTH:
long content_length = _info.getContentLength();
if ((_response!=null || content_length>0 || content_type ) && !_noContent)
if ((response!=null || content_length>0 || content_type ) && !_noContent)
{
// known length but not actually set.
header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
@ -741,12 +752,12 @@ public class HttpGenerator
break;
case NO_CONTENT:
if (_response!=null && status >= 200 && status != 204 && status != 304)
if (response!=null && status >= 200 && status != 204 && status != 304)
header.put(CONTENT_LENGTH_0);
break;
case EOF_CONTENT:
_persistent = _request!=null;
_persistent = request!=null;
break;
case CHUNKED_CONTENT:
@ -780,7 +791,7 @@ public class HttpGenerator
}
// If this is a response, work out persistence
if (_response!=null)
if (response!=null)
{
if (!isPersistent() && (close || _info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal()))
{
@ -814,8 +825,8 @@ public class HttpGenerator
}
}
if (!has_server && status>199 && getSendServerVersion())
header.put(SERVER);
if (status>199)
header.put(SEND[send]);
// end the header.
header.put(HttpTokens.CRLF);
@ -851,7 +862,12 @@ public class HttpGenerator
private static final byte[] HTTP_1_1_SPACE = StringUtil.getBytes(HttpVersion.HTTP_1_1+" ");
private static final byte[] CRLF = StringUtil.getBytes("\015\012");
private static final byte[] TRANSFER_ENCODING_CHUNKED = StringUtil.getBytes("Transfer-Encoding: chunked\015\012");
private static byte[] SERVER = StringUtil.getBytes("Server: Jetty(7.0.x)\015\012");
private static final byte[][] SEND = new byte[][]{
new byte[0],
StringUtil.getBytes("Server: Jetty(9.x.x)\015\012"),
StringUtil.getBytes("X-Powered-By: Jetty(9.x.x)\015\012"),
StringUtil.getBytes("Server: Jetty(9.x.x)\015\012X-Powered-By: Jetty(9.x.x)\015\012")
};
/* ------------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------------- */

View File

@ -108,6 +108,8 @@ public enum HttpHeader
SET_COOKIE2("Set-Cookie2"),
MIME_VERSION("MIME-Version"),
IDENTITY("identity"),
X_POWERED_BY("X-Powered-By"),
UNKNOWN("::UNKNOWN::");

View File

@ -91,6 +91,19 @@ public class HttpTester
{
_version=version;
}
public void setContent(byte[] bytes)
{
try
{
_content=new ByteArrayOutputStream();
_content.write(bytes);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
public void setContent(String content)
{

View File

@ -34,6 +34,7 @@ import java.util.List;
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
import org.eclipse.jetty.util.BufferUtil;
import org.hamcrest.Matchers;
import org.junit.Test;
public class HttpGeneratorServerTest
@ -309,6 +310,54 @@ public class HttpGeneratorServerTest
}
}
}
@Test
public void testSendServerXPoweredBy() throws Exception
{
ByteBuffer header = BufferUtil.allocate(8096);
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false);
HttpFields fields = new HttpFields();
fields.add(HttpHeader.SERVER,"SomeServer");
fields.add(HttpHeader.X_POWERED_BY,"SomePower");
ResponseInfo infoF = new ResponseInfo(HttpVersion.HTTP_1_1, fields, -1, 200, null, false);
String head;
HttpGenerator gen = new HttpGenerator(true,true);
gen.generateResponse(info, header, null, null, true);
head = BufferUtil.toString(header);
BufferUtil.clear(header);
assertThat(head, containsString("HTTP/1.1 200 OK"));
assertThat(head, containsString("Server: Jetty(9.x.x)"));
assertThat(head, containsString("X-Powered-By: Jetty(9.x.x)"));
gen.reset();
gen.generateResponse(infoF, header, null, null, true);
head = BufferUtil.toString(header);
BufferUtil.clear(header);
assertThat(head, containsString("HTTP/1.1 200 OK"));
assertThat(head, not(containsString("Server: Jetty(9.x.x)")));
assertThat(head, containsString("Server: SomeServer"));
assertThat(head, containsString("X-Powered-By: Jetty(9.x.x)"));
assertThat(head, containsString("X-Powered-By: SomePower"));
gen.reset();
gen = new HttpGenerator(false,false);
gen.generateResponse(info, header, null, null, true);
head = BufferUtil.toString(header);
BufferUtil.clear(header);
assertThat(head, containsString("HTTP/1.1 200 OK"));
assertThat(head, not(containsString("Server: Jetty(9.x.x)")));
assertThat(head, not(containsString("X-Powered-By: Jetty(9.x.x)")));
gen.reset();
gen.generateResponse(infoF, header, null, null, true);
head = BufferUtil.toString(header);
BufferUtil.clear(header);
assertThat(head, containsString("HTTP/1.1 200 OK"));
assertThat(head, not(containsString("Server: Jetty(9.x.x)")));
assertThat(head, containsString("Server: SomeServer"));
assertThat(head, not(containsString("X-Powered-By: Jetty(9.x.x)")));
assertThat(head, containsString("X-Powered-By: SomePower"));
gen.reset();
}
@Test
public void testResponseNoContent() throws Exception

View File

@ -618,6 +618,13 @@ public class SslConnection extends AbstractConnection
}
else
{
if (_encryptedInput.hasRemaining())
{
// if there are more encrypted bytes,
// then we need to unwrap more, we don't
// care if net_filled is zero
continue;
}
// we need to wait for more net data
return 0;
}

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
<version>9.0.4-SNAPSHOT</version>
<version>9.0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -54,32 +54,32 @@
<!-- for all configuration that may be set here. -->
<!-- =========================================================== -->
<New id="pushStrategy" class="org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy">
<!-- Uncomment to blacklist browsers for this push strategy. If one of the blacklisted Strings occurs in the
user-agent header sent by the client, push will be disabled for this browser. This is case insensitive" -->
<!--
<Set name="UserAgentBlacklist">
<Array type="String">
<Item>.*(?i)firefox/14.*</Item>
<Item>.*(?i)firefox/15.*</Item>
<Item>.*(?i)firefox/16.*</Item>
</Array>
</Set>
-->
<!-- Uncomment to blacklist browsers for this push strategy. If one of the blacklisted Strings occurs in the
user-agent header sent by the client, push will be disabled for this browser. This is case insensitive" -->
<!--
<Set name="UserAgentBlacklist">
<Array type="String">
<Item>.*(?i)firefox/14.*</Item>
<Item>.*(?i)firefox/15.*</Item>
<Item>.*(?i)firefox/16.*</Item>
</Array>
</Set>
-->
<!-- Uncomment to override default file extensions to push -->
<!--
<Set name="PushRegexps">
<Array type="String">
<Item>.*\.css</Item>
<Item>.*\.js</Item>
<Item>.*\.png</Item>
<Item>.*\.jpg</Item>
<Item>.*\.gif</Item>
</Array>
</Set>
-->
<Set name="referrerPushPeriod">5000</Set>
<Set name="maxAssociatedResources">32</Set>
<!-- Uncomment to override default file extensions to push -->
<!--
<Set name="PushRegexps">
<Array type="String">
<Item>.*\.css</Item>
<Item>.*\.js</Item>
<Item>.*\.png</Item>
<Item>.*\.jpg</Item>
<Item>.*\.gif</Item>
</Array>
</Set>
-->
<Set name="referrerPushPeriod">5000</Set>
<Set name="maxAssociatedResources">32</Set>
</New>
<!-- =========================================================== -->
@ -121,6 +121,10 @@
<Arg name="config">
<Ref refid="httpConfig"/>
</Arg>
<!-- Set the initial window size for this SPDY connector. -->
<!-- See: http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3#TOC-2.6.8-WINDOW_UPDATE -->
<Set name="initialWindowSize">65536</Set>
<!-- Uncomment to enable the push strategy with id "pushStrategy" -->
<!-- <Arg name="pushStrategy"><Ref refid="pushStrategy"/></Arg> -->
</New>
</Item>
@ -131,6 +135,9 @@
<Arg name="config">
<Ref refid="httpConfig"/>
</Arg>
<!-- Set the initial window size for this SPDY connector. -->
<!-- See: http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3#TOC-2.6.8-WINDOW_UPDATE -->
<Set name="initialWindowSize">65536</Set>
</New>
</Item>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.0.4-SNAPSHOT</version>
<version>9.0.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-overlay-deployer</artifactId>

View File

@ -48,6 +48,7 @@ public class HttpConfiguration
private int _securePort;
private String _secureScheme = HttpScheme.HTTPS.asString();
private boolean _sendServerVersion = true; //send Server: header
private boolean _sendXPoweredBy = false; //send X-Powered-By: header
private boolean _sendDateHeader = false; //send Date: header
@ -150,11 +151,22 @@ public class HttpConfiguration
_sendServerVersion = sendServerVersion;
}
@ManagedAttribute("if true, include the server version in HTTP headers")
@ManagedAttribute("if true, send the Server header in responses")
public boolean getSendServerVersion()
{
return _sendServerVersion;
}
public void setSendXPoweredBy (boolean sendXPoweredBy)
{
_sendXPoweredBy=sendXPoweredBy;
}
@ManagedAttribute("if true, send the X-Powered-By header in responses")
public boolean getSendXPoweredBy()
{
return _sendXPoweredBy;
}
public void setSendDateHeader(boolean sendDateHeader)
{

View File

@ -91,8 +91,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
_config = config;
_connector = connector;
_bufferPool = _connector.getByteBufferPool();
_generator = new HttpGenerator();
_generator.setSendServerVersion(_config.getSendServerVersion());
_generator = new HttpGenerator(_config.getSendServerVersion(),_config.getSendXPoweredBy());
_channel = new HttpChannelOverHttp(connector, config, endPoint, this, new Input());
_parser = newHttpParser();

View File

@ -45,10 +45,12 @@ import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.Part;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.MultiPartInputStreamParser;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -259,11 +261,11 @@ public class MultiPartFilter implements Filter
{
try
{
return new String((byte[])o,_encoding);
return getParameterBytesAsString(name, (byte[])o);
}
catch(Exception e)
{
e.printStackTrace();
LOG.warn(e);
}
}
else if (o!=null)
@ -282,9 +284,7 @@ public class MultiPartFilter implements Filter
for ( Object key : _params.keySet() )
{
String[] a = LazyList.toStringArray(getParameter((String)key));
cmap.put((String)key,a);
cmap.put((String)key,getParameterValues((String)key));
}
return Collections.unmodifiableMap(cmap);
@ -318,7 +318,7 @@ public class MultiPartFilter implements Filter
{
try
{
v[i]=new String((byte[])o,_encoding);
v[i]=getParameterBytesAsString(name, (byte[])o);
}
catch(Exception e)
{
@ -341,5 +341,23 @@ public class MultiPartFilter implements Filter
{
_encoding=enc;
}
/* ------------------------------------------------------------------------------- */
private String getParameterBytesAsString (String name, byte[] bytes)
throws UnsupportedEncodingException
{
//check if there is a specific encoding for the parameter
Object ct = _params.getValue(name+CONTENT_TYPE_SUFFIX,0);
//use default if not
String contentType = _encoding;
if (ct != null)
{
String tmp = MimeTypes.getCharsetFromContentType((String)ct);
contentType = (tmp == null?_encoding:tmp);
}
return new String(bytes,contentType);
}
}
}

View File

@ -26,11 +26,13 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.EnumSet;
import java.util.Map;
import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
@ -38,10 +40,12 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletTester;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -105,6 +109,7 @@ public class MultipartFilterTest
public void tearDown() throws Exception
{
tester.stop();
tester=null;
}
@Test
@ -698,7 +703,6 @@ public class MultipartFilterTest
assertTrue(response.getContent().contains("aaaa,bbbbb"));
}
@Test
public void testContentTypeWithCharSet() throws Exception
{
@ -729,7 +733,7 @@ public class MultipartFilterTest
assertTrue(response.getContent().indexOf("brown cow")>=0);
}
/*
* see the testParameterMap test
*
@ -786,6 +790,59 @@ public class MultipartFilterTest
assertTrue(response.getContent().indexOf("brown cow")>=0);
}
public static class TestServletCharSet extends HttpServlet
{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
//test that the multipart content bytes were converted correctly from their charset to unicode
String content = (String)req.getParameter("ttt");
assertNotNull(content);
assertEquals("ttt\u01FCzzz",content);
assertEquals("application/octet-stream; charset=UTF-8",req.getParameter("ttt"+MultiPartFilter.CONTENT_TYPE_SUFFIX));
//test that the parameter map retrieves values as String[]
Map map = req.getParameterMap();
Object o = map.get("ttt");
assertTrue(o.getClass().isArray());
super.doPost(req, resp);
}
}
@Test
public void testWithCharSet()
throws Exception
{
// generated and parsed test
HttpTester.Request request = HttpTester.newRequest();
HttpTester.Response response;
tester.addServlet(TestServletCharSet.class,"/test3");
// test GET
request.setMethod("POST");
request.setVersion("HTTP/1.0");
request.setHeader("Host","tester");
request.setURI("/context/test3");
String boundary="XyXyXy";
request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(("--" + boundary + "\r\n"+
"Content-Disposition: form-data; name=\"ttt\"\r\n"+
"Content-Type: application/octet-stream; charset=UTF-8\r\n\r\n").getBytes());
baos.write("ttt\u01FCzzz".getBytes(StringUtil.__UTF8));
baos.write(("\r\n--" + boundary + "--\r\n\r\n").getBytes());
request.setContent(baos.toByteArray());
response = HttpTester.parseResponse(tester.getResponses(request.generate()));
}
public static class DumpServlet extends HttpServlet
{
private static final long serialVersionUID = 201012011130L;

View File

@ -60,6 +60,7 @@ public class SPDYClient
private volatile SocketAddress bindAddress;
private volatile long idleTimeout = -1;
private volatile int initialWindowSize;
private volatile boolean executeOnFillable;
protected SPDYClient(short version, Factory factory)
{
@ -125,6 +126,16 @@ public class SPDYClient
this.initialWindowSize = initialWindowSize;
}
public boolean isExecuteOnFillable()
{
return executeOnFillable;
}
public void setExecuteOnFillable(boolean executeOnFillable)
{
this.executeOnFillable = executeOnFillable;
}
protected String selectProtocol(List<String> serverProtocols)
{
String protocol = "spdy/" + version;

View File

@ -45,7 +45,7 @@ public class SPDYClientConnectionFactory
Parser parser = new Parser(compressionFactory.newDecompressor());
Generator generator = new Generator(bufferPool, compressionFactory.newCompressor());
SPDYConnection connection = new ClientSPDYConnection(endPoint, bufferPool, parser, factory);
SPDYConnection connection = new ClientSPDYConnection(endPoint, bufferPool, parser, factory, client.isExecuteOnFillable());
FlowControlStrategy flowControlStrategy = client.newFlowControlStrategy();
@ -66,9 +66,10 @@ public class SPDYClientConnectionFactory
{
private final Factory factory;
public ClientSPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Factory factory)
public ClientSPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Factory factory,
boolean executeOnFillable)
{
super(endPoint, bufferPool, parser, factory.getExecutor());
super(endPoint, bufferPool, parser, factory.getExecutor(), executeOnFillable);
this.factory = factory;
}

View File

@ -44,20 +44,26 @@ public class SPDYConnection extends AbstractConnection implements Controller, Id
private volatile ISession session;
private volatile boolean idle = false;
public SPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Executor executor)
public SPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Executor executor,
boolean executeOnFillable)
{
this(endPoint, bufferPool, parser, executor, 8192);
this(endPoint, bufferPool, parser, executor, executeOnFillable, 8192);
}
public SPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Executor executor, int bufferSize)
public SPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Executor executor,
boolean executeOnFillable, int bufferSize)
{
// Since SPDY is multiplexed, onFillable() must never block
// while calling application code. In fact, onFillable()
// always dispatches to a new thread when calling application
// code, so here we can safely pass false as last parameter,
// and avoid to dispatch to onFillable().
super(endPoint, executor, !EXECUTE_ONFILLABLE);
// Since SPDY is multiplexed, onFillable() must never block while calling application code. In fact,
// the SPDY code always dispatches to a new thread when calling application code,
// so here we can safely pass false as last parameter, and avoid to dispatch to onFillable(). The IO
// operation (read, parse, etc.) will not block and will be fast in almost all cases. Big uploads to a server
// however might block the Selector thread for a long time and therefore block other connections to be read.
// This might be a good reason to set executeOnFillable to true.
//
// Due to a jvm bug we've had a Selector thread being stuck at
// sun.nio.ch.FileDispatcherImpl.preClose0(Native Method). That's why we now default executeOnFillable to
// true even if for most use cases it is faster to not dispatch the IO events.
super(endPoint, executor, executeOnFillable);
this.bufferPool = bufferPool;
this.parser = parser;
onIdle(true);

View File

@ -8,128 +8,150 @@
<!-- ============================================================= -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- =========================================================== -->
<!-- Enables NPN debugging on System.err -->
<!-- ===========================================================
<Set class="org.eclipse.jetty.npn.NextProtoNego" name="debug" type="boolean">true</Set>
-->
<!-- =========================================================== -->
<!-- Create a push strategy which can be used by reference by -->
<!-- individual connection factories below. -->
<!-- -->
<!-- Consult the javadoc of o.e.j.spdy.server.http.ReferrerPushStrategy -->
<!-- for all configuration that may be set here. -->
<!-- =========================================================== -->
<New id="pushStrategy" class="org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy">
<!-- Uncomment to blacklist browsers for this push strategy. If one of the blacklisted Strings occurs in the
user-agent header sent by the client, push will be disabled for this browser. This is case insensitive" -->
<!--
<Set name="UserAgentBlacklist">
<Array type="String">
<Item>.*(?i)firefox/14.*</Item>
<Item>.*(?i)firefox/15.*</Item>
<Item>.*(?i)firefox/16.*</Item>
</Array>
</Set>
<!-- =========================================================== -->
<!-- Enables NPN debugging on System.err -->
<!-- ===========================================================
<Set class="org.eclipse.jetty.npn.NextProtoNego" name="debug" type="boolean">true</Set>
-->
<!-- Uncomment to override default file extensions to push -->
<!--
<Set name="PushRegexps">
<Array type="String">
<Item>.*\.css</Item>
<Item>.*\.js</Item>
<Item>.*\.png</Item>
<Item>.*\.jpg</Item>
<Item>.*\.gif</Item>
</Array>
</Set>
-->
<Set name="referrerPushPeriod">5000</Set>
<Set name="maxAssociatedResources">32</Set>
</New>
<!-- =========================================================== -->
<!-- Create a push strategy which can be used by reference by -->
<!-- individual connection factories below. -->
<!-- -->
<!-- Consult the javadoc of o.e.j.spdy.server.http.ReferrerPushStrategy -->
<!-- for all configuration that may be set here. -->
<!-- =========================================================== -->
<New id="pushStrategy" class="org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy">
<!-- Uncomment to blacklist browsers for this push strategy. If one of the blacklisted Strings occurs in the
user-agent header sent by the client, push will be disabled for this browser. This is case insensitive" -->
<!--
<Set name="UserAgentBlacklist">
<Array type="String">
<Item>.*(?i)firefox/14.*</Item>
<Item>.*(?i)firefox/15.*</Item>
<Item>.*(?i)firefox/16.*</Item>
</Array>
</Set>
-->
<!-- =========================================================== -->
<!-- Add a SPDY/HTTPS Connector. -->
<!-- Configure an o.e.j.server.ServerConnector with connection -->
<!-- factories for TLS (aka SSL), NPN, SPDY and HTTP to provide -->
<!-- a connector that can accept HTTPS or SPDY connections. -->
<!-- -->
<!-- All accepted TLS connections are initially wired to a NPN -->
<!-- connection, which attempts to use a TLS extension to -->
<!-- negotiation the protocol. If NPN is not supported by -->
<!-- the client, then the NPN connection is replaced by a HTTP -->
<!-- connection. If a specific protocol version (eg spdy/3) is -->
<!-- negotiated by NPN, then the appropriate connection factory -->
<!-- is used to create a connection to replace the NPN connection-->
<!-- -->
<!-- The final result is a SPDY or HTTP connection wired behind -->
<!-- a TLS (aka SSL) connection. -->
<!-- -->
<!-- Consult the javadoc of o.e.j.server.ServerConnector and the -->
<!-- specific connection factory types for all configuration -->
<!-- that may be set here. -->
<!-- =========================================================== -->
<Call id="spdyConnector" name="addConnector">
<Arg>
<New class="org.eclipse.jetty.server.ServerConnector">
<Arg name="server"><Ref refid="Server" /></Arg>
<Arg name="factories">
<Array type="org.eclipse.jetty.server.ConnectionFactory">
<!-- Uncomment to override default file extensions to push -->
<!--
<Set name="PushRegexps">
<Array type="String">
<Item>.*\.css</Item>
<Item>.*\.js</Item>
<Item>.*\.png</Item>
<Item>.*\.jpg</Item>
<Item>.*\.gif</Item>
</Array>
</Set>
-->
<Set name="referrerPushPeriod">5000</Set>
<Set name="maxAssociatedResources">32</Set>
</New>
<!-- SSL Connection factory with NPN as next protocol -->
<Item>
<New class="org.eclipse.jetty.server.SslConnectionFactory">
<Arg name="next">npn</Arg>
<Arg name="sslContextFactory"><Ref refid="sslContextFactory" /></Arg>
</New>
</Item>
<!-- =========================================================== -->
<!-- Add a SPDY/HTTPS Connector. -->
<!-- Configure an o.e.j.server.ServerConnector with connection -->
<!-- factories for TLS (aka SSL), NPN, SPDY and HTTP to provide -->
<!-- a connector that can accept HTTPS or SPDY connections. -->
<!-- -->
<!-- All accepted TLS connections are initially wired to a NPN -->
<!-- connection, which attempts to use a TLS extension to -->
<!-- negotiation the protocol. If NPN is not supported by -->
<!-- the client, then the NPN connection is replaced by a HTTP -->
<!-- connection. If a specific protocol version (eg spdy/3) is -->
<!-- negotiated by NPN, then the appropriate connection factory -->
<!-- is used to create a connection to replace the NPN connection-->
<!-- -->
<!-- The final result is a SPDY or HTTP connection wired behind -->
<!-- a TLS (aka SSL) connection. -->
<!-- -->
<!-- Consult the javadoc of o.e.j.server.ServerConnector and the -->
<!-- specific connection factory types for all configuration -->
<!-- that may be set here. -->
<!-- =========================================================== -->
<Call id="spdyConnector" name="addConnector">
<Arg>
<New class="org.eclipse.jetty.server.ServerConnector">
<Arg name="server">
<Ref refid="Server"/>
</Arg>
<Arg name="factories">
<Array type="org.eclipse.jetty.server.ConnectionFactory">
<!-- NPN Connection factory with HTTP as default protocol -->
<Item>
<New class="org.eclipse.jetty.spdy.server.NPNServerConnectionFactory">
<Arg name="protocols"><Array type="String">
<Item>spdy/3</Item>
<Item>spdy/2</Item>
<Item>http/1.1</Item>
</Array></Arg>
<Set name="defaultProtocol">http/1.1</Set>
</New>
</Item>
<!-- SSL Connection factory with NPN as next protocol -->
<Item>
<New class="org.eclipse.jetty.server.SslConnectionFactory">
<Arg name="next">npn</Arg>
<Arg name="sslContextFactory">
<Ref refid="sslContextFactory"/>
</Arg>
</New>
</Item>
<!-- SPDY/3 Connection factory -->
<Item>
<New class="org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory">
<Arg name="version" type="int">3</Arg>
<Arg name="config"><Ref refid="sslHttpConfig" /></Arg>
<!-- Uncomment to enable ReferrerPushStrategy -->
<!--<Arg name="pushStrategy"><Ref refid="pushStrategy"/></Arg>-->
</New>
</Item>
<!-- NPN Connection factory with HTTP as default protocol -->
<Item>
<New class="org.eclipse.jetty.spdy.server.NPNServerConnectionFactory">
<Arg name="protocols">
<Array type="String">
<Item>spdy/3</Item>
<Item>spdy/2</Item>
<Item>http/1.1</Item>
</Array>
</Arg>
<Set name="defaultProtocol">http/1.1</Set>
</New>
</Item>
<!-- SPDY/2 Connection factory -->
<Item>
<New class="org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory">
<Arg name="version" type="int">2</Arg>
<Arg name="config"><Ref refid="sslHttpConfig" /></Arg>
</New>
</Item>
<!-- SPDY/3 Connection factory -->
<Item>
<New class="org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory">
<Arg name="version" type="int">3</Arg>
<Arg name="config">
<Ref refid="sslHttpConfig"/>
</Arg>
<!-- Set the initial window size for this SPDY connector. -->
<!-- See: http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3#TOC-2.6.8-WINDOW_UPDATE -->
<Set name="initialWindowSize">65536</Set>
<!-- Uncomment to enable ReferrerPushStrategy -->
<!--<Arg name="pushStrategy"><Ref refid="pushStrategy"/></Arg>-->
</New>
</Item>
<!-- HTTP Connection factory -->
<Item>
<New class="org.eclipse.jetty.server.HttpConnectionFactory">
<Arg name="config"><Ref refid="sslHttpConfig" /></Arg>
</New>
</Item>
</Array>
<!-- SPDY/2 Connection factory -->
<Item>
<New class="org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory">
<Arg name="version" type="int">2</Arg>
<Arg name="config">
<Ref refid="sslHttpConfig"/>
</Arg>
<!-- Set the initial window size for this SPDY connector. -->
<!-- See: http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3#TOC-2.6.8-WINDOW_UPDATE -->
<Set name="initialWindowSize">65536</Set>
</New>
</Item>
<!-- HTTP Connection factory -->
<Item>
<New class="org.eclipse.jetty.server.HttpConnectionFactory">
<Arg name="config">
<Ref refid="sslHttpConfig"/>
</Arg>
</New>
</Item>
</Array>
</Arg>
<Set name="host">
<Property name="jetty.host"/>
</Set>
<Set name="port">
<Property name="jetty.spdy.port" default="8443"/>
</Set>
<Set name="idleTimeout">30000</Set>
</New>
</Arg>
<Set name="host"><Property name="jetty.host" /></Set>
<Set name="port"><Property name="jetty.spdy.port" default="8443" /></Set>
<Set name="idleTimeout">30000</Set>
</New>
</Arg>
</Call>
</Call>
</Configure>

View File

@ -98,7 +98,7 @@ public class HTTPSPDYServerConnectionFactory extends SPDYServerConnectionFactory
Fields headers = synInfo.getHeaders();
// According to SPDY/3 spec section 3.2.1 user-agents MUST support gzip compression. Firefox omits the
// accep-encoding header as it is redundant to negotiate gzip compression support with the server,
// accept-encoding header as it is redundant to negotiate gzip compression support with the server,
// if clients have to accept it.
// So we inject the accept-encoding header here, even if not set by the client. This will enforce SPDY
// clients to follow the spec and enable gzip compression if GzipFilter or the like is enabled.

View File

@ -69,6 +69,7 @@ public class SPDYServerConnectionFactory extends AbstractConnectionFactory
private final short version;
private final ServerSessionFrameListener listener;
private int initialWindowSize;
private boolean executeOnFillable = true;
private final Queue<Session> sessions = new ConcurrentLinkedQueue<>();
public SPDYServerConnectionFactory(int version)
@ -103,14 +104,15 @@ public class SPDYServerConnectionFactory extends AbstractConnectionFactory
Generator generator = new Generator(connector.getByteBufferPool(), compressionFactory.newCompressor());
ServerSessionFrameListener listener = provideServerSessionFrameListener(connector, endPoint);
SPDYConnection connection = new ServerSPDYConnection(connector, endPoint, parser, listener, getInputBufferSize());
SPDYConnection connection = new ServerSPDYConnection(connector, endPoint, parser, listener,
executeOnFillable, getInputBufferSize());
FlowControlStrategy flowControlStrategy = newFlowControlStrategy(version);
StandardSession session = new StandardSession(getVersion(), connector.getByteBufferPool(),
connector.getExecutor(), connector.getScheduler(), connection, endPoint, connection, 2, listener,
generator, flowControlStrategy);
session.setWindowSize(getInitialWindowSize());
session.setWindowSize(initialWindowSize);
parser.addListener(session);
connection.setSession(session);
@ -140,6 +142,17 @@ public class SPDYServerConnectionFactory extends AbstractConnectionFactory
this.initialWindowSize = initialWindowSize;
}
@ManagedAttribute("Execute onFillable")
public boolean isExecuteOnFillable()
{
return executeOnFillable;
}
public void setExecuteOnFillable(boolean executeOnFillable)
{
this.executeOnFillable = executeOnFillable;
}
protected boolean sessionOpened(Session session)
{
// Add sessions only if the connector is not stopping
@ -177,9 +190,11 @@ public class SPDYServerConnectionFactory extends AbstractConnectionFactory
private final ServerSessionFrameListener listener;
private final AtomicBoolean connected = new AtomicBoolean();
private ServerSPDYConnection(Connector connector, EndPoint endPoint, Parser parser, ServerSessionFrameListener listener, int bufferSize)
private ServerSPDYConnection(Connector connector, EndPoint endPoint, Parser parser,
ServerSessionFrameListener listener, boolean executeOnFillable, int bufferSize)
{
super(endPoint, connector.getByteBufferPool(), parser, connector.getExecutor(), bufferSize);
super(endPoint, connector.getByteBufferPool(), parser, connector.getExecutor(),
executeOnFillable, bufferSize);
this.listener = listener;
}

View File

@ -24,7 +24,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.GoAwayInfo;
import org.eclipse.jetty.spdy.api.GoAwayResultInfo;
@ -41,7 +40,6 @@ import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.FuturePromise;
import org.hamcrest.CoreMatchers;
import org.junit.Assert;
import org.junit.Test;
@ -223,7 +221,8 @@ public class GoAwayTest extends AbstractTest
}
catch (ExecutionException x)
{
Assert.assertThat(x.getCause(), CoreMatchers.instanceOf(EofException.class));
// doesn't matter which exception we get, it's important that the data is not been written and the
// previous assertion is true
}
// The last good stream is the second, because it was received by the server

View File

@ -141,6 +141,21 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
return String.format("%s@%x",FlushInvoker.class.getSimpleName(),hashCode());
}
}
public class OnDisconnectCallback implements WriteCallback
{
@Override
public void writeFailed(Throwable x)
{
disconnect();
}
@Override
public void writeSuccess()
{
disconnect();
}
}
public static class Stats
{
@ -233,6 +248,7 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
@Override
public void disconnect()
{
LOG.debug("{} disconnect()", policy.getBehavior());
synchronized (writeBytes)
{
if (!writeBytes.isClosed())
@ -433,7 +449,7 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
@Override
public void onConnectionStateChange(ConnectionState state)
{
LOG.debug("Connection State Change: {}",state);
LOG.debug("{} Connection State Change: {}",policy.getBehavior(),state);
switch (state)
{
case OPEN:
@ -446,7 +462,7 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
case CLOSING:
CloseInfo close = ioState.getCloseInfo();
// append close frame
outgoingFrame(close.asFrame(),null);
outgoingFrame(close.asFrame(),new OnDisconnectCallback());
default:
break;
}

View File

@ -182,11 +182,13 @@ public class IOState
*/
public void onCloseLocal(CloseInfo close)
{
LOG.debug("onCloseLocal({})",close);
ConnectionState event = null;
ConnectionState initialState = this.state;
if (initialState == ConnectionState.CLOSED)
{
// already closed
LOG.debug("already closed");
return;
}
@ -224,15 +226,19 @@ public class IOState
event = this.state;
}
}
LOG.debug("event = {}",event);
// Only notify on state change events
if (event != null)
{
LOG.debug("notifying state listeners: {}",event);
notifyStateListeners(event);
// if SHUTDOWN, we don't expect an answer.
if (close.getStatusCode() == StatusCode.SHUTDOWN)
// if harsh, we don't expect an answer.
if (close.isHarsh())
{
LOG.debug("Harsh close, disconnecting");
synchronized (this.state)
{
this.state = ConnectionState.CLOSED;
@ -253,6 +259,7 @@ public class IOState
*/
public void onCloseRemote(CloseInfo close)
{
LOG.debug("onCloseRemote({})",close);
ConnectionState event = null;
synchronized (this.state)
{

View File

@ -27,6 +27,7 @@ import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.thread.Scheduler;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
import org.eclipse.jetty.websocket.common.ConnectionState;
import org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection;
public class WebSocketServerConnection extends AbstractWebSocketConnection
@ -74,7 +75,7 @@ public class WebSocketServerConnection extends AbstractWebSocketConnection
}
super.onOpen();
}
@Override
public void setNextIncomingFrames(IncomingFrames incoming)
{

View File

@ -23,8 +23,12 @@ import static org.hamcrest.Matchers.*;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.OpCode;
import org.eclipse.jetty.websocket.common.WebSocketFrame;
import org.eclipse.jetty.websocket.server.blockhead.BlockheadClient;
import org.eclipse.jetty.websocket.server.helper.IncomingFramesCapture;
import org.eclipse.jetty.websocket.server.helper.RFCSocket;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
@ -69,7 +73,7 @@ public class IdleTimeoutTest
{
BlockheadClient client = new BlockheadClient(server.getServerUri());
client.setProtocols("onConnect");
client.setTimeout(TimeUnit.MILLISECONDS,1500);
client.setTimeout(TimeUnit.MILLISECONDS,2500);
try
{
client.connect();
@ -83,13 +87,15 @@ public class IdleTimeoutTest
// Write to server (the server should be timed out and disconnect now)
client.write(WebSocketFrame.text("Hello"));
// now attempt to read 2 echoed frames from server (shouldn't work)
client.readFrames(2,TimeUnit.MILLISECONDS,1500);
Assert.fail("Should have resulted in IOException");
}
catch (IOException e)
{
Assert.assertThat("IOException",e.getMessage(),anyOf(containsString("closed"),containsString("disconnected")));
// now read 1 frame from server (should be close frame)
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,1500);
WebSocketFrame frame = capture.getFrames().poll();
Assert.assertThat("Was close frame", frame.getOpCode(), is(OpCode.CLOSE));
CloseInfo close = new CloseInfo(frame);
Assert.assertThat("Close.code", close.getStatusCode(), is(StatusCode.SHUTDOWN));
Assert.assertThat("Close.reason", close.getReason(), containsString("Idle Timeout"));
client.expectServerDisconnect();
}
finally
{

View File

@ -25,6 +25,9 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.toolchain.test.EventQueue;
import org.eclipse.jetty.toolchain.test.TestTracker;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.server.helper.CaptureSocket;
@ -32,10 +35,14 @@ import org.eclipse.jetty.websocket.server.helper.SessionServlet;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
public class WebSocketOverSSLTest
{
@Rule
public TestTracker tracker = new TestTracker();
private static SimpleServletServer server;
@BeforeClass
@ -65,7 +72,9 @@ public class WebSocketOverSSLTest
client.start();
CaptureSocket clientSocket = new CaptureSocket();
Future<Session> fut = client.connect(clientSocket,server.getServerUri());
URI requestUri = server.getServerUri();
System.err.printf("Request URI: %s%n",requestUri.toASCIIString());
Future<Session> fut = client.connect(clientSocket,requestUri);
// wait for connect
Session session = fut.get(3,TimeUnit.SECONDS);
@ -142,13 +151,13 @@ public class WebSocketOverSSLTest
CaptureSocket clientSocket = new CaptureSocket();
URI requestUri = server.getServerUri().resolve("/deep?a=b");
System.err.printf("Request URI: %s%n",requestUri.toASCIIString());
client.connect(clientSocket,requestUri);
Future<Session> fut = client.connect(clientSocket,requestUri);
// wait for connect
clientSocket.awaitConnected(5000);
Session session = fut.get(5,TimeUnit.SECONDS);
// Generate text frame
clientSocket.getRemote().sendString("session.upgradeRequest.requestURI");
session.getRemote().sendString("session.upgradeRequest.requestURI");
// Read frame (hopefully text frame)
clientSocket.messages.awaitEventCount(1,500,TimeUnit.MILLISECONDS);

View File

@ -62,13 +62,18 @@ public class Fuzzer
PER_FRAME,
SLOW
}
public static enum DisconnectMode
{
/** Disconnect occurred after a proper close handshake */
CLEAN,
/** Disconnect occurred in a harsh manner, without a close handshake */
UNCLEAN
}
private static final int KBYTE = 1024;
private static final int MBYTE = KBYTE * KBYTE;
public static final boolean CLEAN_CLOSE = true;
public static final boolean NOT_CLEAN_CLOSE = false;
private static final Logger LOG = Log.getLogger(Fuzzer.class);
// Client side framing mask
@ -89,6 +94,7 @@ public class Fuzzer
policy.setMaxTextMessageSize(bigMessageSize);
policy.setMaxBinaryMessageSize(bigMessageSize);
policy.setIdleTimeout(5000);
this.client = new BlockheadClient(policy,testcase.getServer().getServerUri());
this.generator = testcase.getLaxGenerator();
@ -184,35 +190,21 @@ public class Fuzzer
// TODO Should test for no more frames. success if connection closed.
}
public void expectServerClose(boolean wasClean) throws IOException, InterruptedException
public void expectServerDisconnect(DisconnectMode mode)
{
// we expect that the close handshake to have occurred and the server should have closed the connection
try
{
ByteBuffer buf = ByteBuffer.wrap(new byte[]
{ 0x00 });
BufferUtil.flipToFill(buf);
int len = client.read(buf);
Assert.assertThat("Server has not closed socket",len,lessThanOrEqualTo(0));
}
catch (IOException e)
{
// valid path
}
client.expectServerDisconnect();
IOState ios = client.getIOState();
if (wasClean)
switch (mode)
{
Assert.assertTrue(ios.wasRemoteCloseInitiated());
Assert.assertTrue(ios.wasCleanClose());
case CLEAN:
Assert.assertTrue(ios.wasRemoteCloseInitiated());
Assert.assertTrue(ios.wasCleanClose());
break;
case UNCLEAN:
Assert.assertTrue(ios.wasRemoteCloseInitiated());
break;
}
else
{
Assert.assertTrue(ios.wasRemoteCloseInitiated());
}
}
public CloseState getCloseState()
@ -343,20 +335,6 @@ public class Fuzzer
}
}
public void sendExpectingIOException(ByteBuffer part3)
{
try
{
send(part3);
Assert.fail("Expected a IOException on this send");
}
catch (IOException ignore)
{
// Send, but expect the send to fail with a IOException.
// Usually, this is a SocketException("Socket Closed") condition.
}
}
private void setClientMask(WebSocketFrame f)
{
if (LOG.isDebugEnabled())

View File

@ -51,7 +51,7 @@ public class TestABCase1 extends AbstractABCase
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
fuzzer.expectServerClose(Fuzzer.CLEAN_CLOSE);
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
}
finally
{
@ -83,6 +83,7 @@ public class TestABCase1 extends AbstractABCase
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
}
finally
{
@ -114,6 +115,7 @@ public class TestABCase1 extends AbstractABCase
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
}
finally
{
@ -145,6 +147,7 @@ public class TestABCase1 extends AbstractABCase
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
}
finally
{
@ -176,6 +179,7 @@ public class TestABCase1 extends AbstractABCase
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
}
finally
{
@ -207,6 +211,7 @@ public class TestABCase1 extends AbstractABCase
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
}
finally
{
@ -238,6 +243,7 @@ public class TestABCase1 extends AbstractABCase
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
}
finally
{
@ -275,6 +281,7 @@ public class TestABCase1 extends AbstractABCase
fuzzer.setSlowSendSegmentSize(segmentSize);
fuzzer.send(send);
fuzzer.expect(expect);
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
}
finally
{
@ -303,6 +310,7 @@ public class TestABCase1 extends AbstractABCase
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
}
finally
{
@ -334,6 +342,7 @@ public class TestABCase1 extends AbstractABCase
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
}
finally
{
@ -365,6 +374,7 @@ public class TestABCase1 extends AbstractABCase
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
}
finally
{
@ -396,6 +406,7 @@ public class TestABCase1 extends AbstractABCase
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
}
finally
{
@ -427,6 +438,7 @@ public class TestABCase1 extends AbstractABCase
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
}
finally
{
@ -458,6 +470,7 @@ public class TestABCase1 extends AbstractABCase
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
}
finally
{
@ -489,6 +502,7 @@ public class TestABCase1 extends AbstractABCase
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
}
finally
{
@ -526,6 +540,7 @@ public class TestABCase1 extends AbstractABCase
fuzzer.setSlowSendSegmentSize(segmentSize);
fuzzer.send(send);
fuzzer.expect(expect);
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
}
finally
{

View File

@ -24,6 +24,7 @@ import java.util.List;
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.OpCode;
@ -236,32 +237,32 @@ public class TestABCase2 extends AbstractABCase
@Test
public void testCase2_5() throws Exception
{
// Disable Long Stacks from Parser (we know this test will throw an exception)
enableStacks(Parser.class,false);
byte payload[] = new byte[126]; // intentionally too big
Arrays.fill(payload,(byte)'5');
List<WebSocketFrame> send = new ArrayList<>();
// trick websocket frame into making extra large payload for ping
send.add(WebSocketFrame.binary(payload).setOpCode(OpCode.PING));
send.add(new CloseInfo(StatusCode.NORMAL,"Test 2.5").asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
try(StacklessLogging scope = new StacklessLogging(Parser.class))
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
enableStacks(Parser.class,true);
fuzzer.close();
byte payload[] = new byte[126]; // intentionally too big
Arrays.fill(payload,(byte)'5');
List<WebSocketFrame> send = new ArrayList<>();
// trick websocket frame into making extra large payload for ping
send.add(WebSocketFrame.binary(payload).setOpCode(OpCode.PING));
send.add(new CloseInfo(StatusCode.NORMAL,"Test 2.5").asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
}
finally
{
fuzzer.close();
}
}
}

View File

@ -28,6 +28,7 @@ import org.eclipse.jetty.toolchain.test.annotation.Slow;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.OpCode;
@ -358,54 +359,55 @@ public class TestABCase6 extends AbstractABCase
public void testCase6_4_3() throws Exception
{
// Disable Long Stacks from Parser (we know this test will throw an exception)
enableStacks(Parser.class,false);
ByteBuffer payload = ByteBuffer.allocate(64);
BufferUtil.clearToFill(payload);
payload.put(TypeUtil.fromHexString("cebae1bdb9cf83cebcceb5")); // good
payload.put(TypeUtil.fromHexString("f4908080")); // INVALID
payload.put(TypeUtil.fromHexString("656469746564")); // good
BufferUtil.flipToFlush(payload,0);
List<WebSocketFrame> send = new ArrayList<>();
send.add(new WebSocketFrame(OpCode.TEXT).setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
try(StacklessLogging scope = new StacklessLogging(Parser.class))
{
fuzzer.connect();
ByteBuffer payload = ByteBuffer.allocate(64);
BufferUtil.clearToFill(payload);
payload.put(TypeUtil.fromHexString("cebae1bdb9cf83cebcceb5")); // good
payload.put(TypeUtil.fromHexString("f4908080")); // INVALID
payload.put(TypeUtil.fromHexString("656469746564")); // good
BufferUtil.flipToFlush(payload,0);
ByteBuffer net = fuzzer.asNetworkBuffer(send);
List<WebSocketFrame> send = new ArrayList<>();
send.add(new WebSocketFrame(OpCode.TEXT).setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
int splits[] =
{ 17, 21, net.limit() };
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
ByteBuffer part1 = net.slice(); // Header + good UTF
part1.limit(splits[0]);
ByteBuffer part2 = net.slice(); // invalid UTF
part2.position(splits[0]);
part2.limit(splits[1]);
ByteBuffer part3 = net.slice(); // good UTF
part3.position(splits[1]);
part3.limit(splits[2]);
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.send(part1); // the header + good utf
TimeUnit.SECONDS.sleep(1);
fuzzer.send(part2); // the bad UTF
ByteBuffer net = fuzzer.asNetworkBuffer(send);
fuzzer.expect(expect);
int splits[] =
{ 17, 21, net.limit() };
TimeUnit.SECONDS.sleep(1);
fuzzer.sendExpectingIOException(part3); // the rest (shouldn't work)
}
finally
{
enableStacks(Parser.class,true);
fuzzer.close();
ByteBuffer part1 = net.slice(); // Header + good UTF
part1.limit(splits[0]);
ByteBuffer part2 = net.slice(); // invalid UTF
part2.position(splits[0]);
part2.limit(splits[1]);
ByteBuffer part3 = net.slice(); // good UTF
part3.position(splits[1]);
part3.limit(splits[2]);
fuzzer.send(part1); // the header + good utf
TimeUnit.SECONDS.sleep(1);
fuzzer.send(part2); // the bad UTF
fuzzer.expect(expect);
TimeUnit.SECONDS.sleep(1);
fuzzer.send(part3); // the rest (shouldn't work)
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.UNCLEAN);
}
finally
{
fuzzer.close();
}
}
}

View File

@ -168,7 +168,7 @@ public class TestABCase6_BadUTF extends AbstractABCase
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
fuzzer.expectServerClose(Fuzzer.NOT_CLEAN_CLOSE);
fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.UNCLEAN);
}
finally
{

View File

@ -29,6 +29,7 @@ import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
@ -124,10 +125,8 @@ public class BlockheadClient implements IncomingFrames, OutgoingFrames, Connecti
{
Assert.assertThat("Websocket URI scheme",destWebsocketURI.getScheme(),anyOf(is("ws"),is("wss")));
this.destWebsocketURI = destWebsocketURI;
String scheme = "http";
if (destWebsocketURI.getScheme().equals("wss"))
{
scheme = "https";
throw new RuntimeException("Sorry, BlockheadClient does not support SSL");
}
this.destHttpURI = WSURI.toHttp(destWebsocketURI);
@ -414,7 +413,8 @@ public class BlockheadClient implements IncomingFrames, OutgoingFrames, Connecti
switch (state)
{
case CLOSED:
this.disconnect();
// Per Spec, client should not initiate disconnect on its own
// this.disconnect();
break;
case CLOSING:
if (ioState.wasRemoteCloseInitiated())
@ -464,6 +464,36 @@ public class BlockheadClient implements IncomingFrames, OutgoingFrames, Connecti
}
}
public void expectServerDisconnect()
{
if (eof)
{
return;
}
try
{
int len = in.read();
if (len == (-1))
{
// we are disconnected
eof = true;
return;
}
Assert.assertThat("Expecting no data and proper socket disconnect (issued from server)",len,is(-1));
}
catch (SocketTimeoutException e)
{
LOG.warn(e);
Assert.fail("Expected a server initiated disconnect, instead the read timed out");
}
catch (IOException e)
{
// acceptable path
}
}
public int read(ByteBuffer buf) throws IOException
{
if (eof)

View File

@ -23,6 +23,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.toolchain.test.EventQueue;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
public class CaptureSocket extends WebSocketAdapter
@ -45,12 +46,10 @@ public class CaptureSocket extends WebSocketAdapter
getSession().close();
}
public void onClose(int closeCode, String message)
{
}
public void onOpen(Connection connection)
@Override
public void onWebSocketConnect(Session sess)
{
super.onWebSocketConnect(sess);
latch.countDown();
}