aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Becker2012-07-27 09:51:14 (EDT)
committerThomas Becker2012-07-27 09:51:14 (EDT)
commitb36a6cc9ccf28eea4a2e084e5972ca6f0a81dd4f (patch)
tree37f8cacaa6d89527e4bffd07b32b4a9d1cbe0f9a
parentfbcb14f2974dd8d2f2eaf45089306329771bf4c3 (diff)
downloadorg.eclipse.jetty.project-b36a6cc9ccf28eea4a2e084e5972ca6f0a81dd4f.zip
org.eclipse.jetty.project-b36a6cc9ccf28eea4a2e084e5972ca6f0a81dd4f.tar.gz
org.eclipse.jetty.project-b36a6cc9ccf28eea4a2e084e5972ca6f0a81dd4f.tar.bz2
jetty-9 checkout jetty-spdy from jetty-8
-rw-r--r--jetty-spdy/pom.xml4
-rw-r--r--jetty-spdy/spdy-core/pom.xml43
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/ByteBufferPool.java46
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/CompressionDictionary.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/CompressionFactory.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Controller.java31
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/FlowControlStrategy.java86
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/ISession.java33
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IStream.java52
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IdleListener.java28
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Promise.java39
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/PushSynInfo.java12
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/SPDYv3FlowControlStrategy.java84
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/SessionException.java28
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardByteBufferPool.java97
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardCompressionFactory.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java460
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardStream.java195
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StreamException.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/ByteBufferDataInfo.java33
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/BytesDataInfo.java54
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/DataInfo.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/GoAwayInfo.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Handler.java56
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Headers.java48
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/HeadersInfo.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/PingInfo.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/ReplyInfo.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/RstInfo.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SPDY.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SPDYException.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Session.java91
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionFrameListener.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionStatus.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Settings.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SettingsInfo.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Stream.java55
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StreamFrameListener.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StreamStatus.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StringDataInfo.java34
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SynInfo.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/server/ServerSessionFrameListener.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/ControlFrame.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/ControlFrameType.java30
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/CredentialFrame.java46
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/DataFrame.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/GoAwayFrame.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/HeadersFrame.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/NoOpFrame.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/PingFrame.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/RstStreamFrame.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SettingsFrame.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SynReplyFrame.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SynStreamFrame.java36
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/WindowUpdateFrame.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/ControlFrameGenerator.java29
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/CredentialGenerator.java80
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/DataFrameGenerator.java30
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/Generator.java30
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/GoAwayGenerator.java29
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersBlockGenerator.java29
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersGenerator.java34
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/NoOpGenerator.java29
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/PingGenerator.java29
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/RstStreamGenerator.java29
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SettingsGenerator.java29
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SynReplyGenerator.java29
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SynStreamGenerator.java31
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/WindowUpdateGenerator.java29
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/ControlFrameBodyParser.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/ControlFrameParser.java28
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/CredentialBodyParser.java269
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/DataFrameParser.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/GoAwayBodyParser.java39
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBlockParser.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBodyParser.java75
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/NoOpBodyParser.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/Parser.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/PingBodyParser.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/RstStreamBodyParser.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SettingsBodyParser.java28
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SynReplyBodyParser.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SynStreamBodyParser.java34
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/UnknownControlFrameBodyParser.java27
-rw-r--r--jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/WindowUpdateBodyParser.java27
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/AsyncTimeoutTest.java47
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/StandardSessionTest.java170
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/StandardStreamTest.java85
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ClientUsageTest.java40
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ServerUsageTest.java32
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/CredentialGenerateParseTest.java99
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/DataGenerateParseTest.java29
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/GoAwayGenerateParseTest.java29
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/HeadersGenerateParseTest.java123
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/NoOpGenerateParseTest.java29
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/PingGenerateParseTest.java29
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/RstStreamGenerateParseTest.java41
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SettingsGenerateParseTest.java29
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SynReplyGenerateParseTest.java29
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SynStreamGenerateParseTest.java37
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/TestSPDYParserListener.java27
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/WindowUpdateGenerateParseTest.java29
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/LiveChromiumRequestParserTest.java27
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/ParseVersusCacheBenchmarkTest.java27
-rw-r--r--jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/UnknownControlFrameTest.java17
-rw-r--r--jetty-spdy/spdy-core/src/test/resources/keystore.jksbin0 -> 2206 bytes
-rw-r--r--jetty-spdy/spdy-core/src/test/resources/truststore.jksbin0 -> 916 bytes
-rw-r--r--jetty-spdy/spdy-jetty-http-webapp/pom.xml46
-rw-r--r--jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy-proxy.xml98
-rw-r--r--jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy.xml54
-rw-r--r--jetty-spdy/spdy-jetty-http/pom.xml14
-rw-r--r--jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/AbstractHTTPSPDYServerConnector.java61
-rw-r--r--jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYHeader.java73
-rw-r--r--jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYServerConnector.java105
-rw-r--r--jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/PushStrategy.java28
-rw-r--r--jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategy.java262
-rw-r--r--jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPAsyncConnectionFactory.java39
-rw-r--r--jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnection.java229
-rw-r--r--jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnectionFactory.java36
-rw-r--r--jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/HTTPSPDYProxyConnector.java39
-rw-r--r--jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyEngine.java94
-rw-r--r--jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyEngineSelector.java169
-rw-r--r--jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPAsyncConnectionFactory.java41
-rw-r--r--jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPSPDYAsyncConnection.java337
-rw-r--r--jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/SPDYProxyEngine.java514
-rw-r--r--jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/AbstractHTTPSPDYTest.java61
-rw-r--r--jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ConcurrentStreamsTest.java44
-rw-r--r--jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ProtocolNegotiationTest.java55
-rw-r--r--jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/PushStrategyBenchmarkTest.java395
-rw-r--r--jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyUnitTest.java119
-rw-r--r--jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyV2Test.java797
-rw-r--r--jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyV3Test.java26
-rw-r--r--jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/SSLExternalServerTest.java94
-rw-r--r--jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYv2Test.java1272
-rw-r--r--jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYv3Test.java26
-rw-r--r--jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPSPDYv2Test.java764
-rw-r--r--jetty-spdy/spdy-jetty/pom.xml3
-rw-r--r--jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/AsyncConnectionFactory.java30
-rw-r--r--jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/EmptyAsyncConnection.java66
-rw-r--r--jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/EmptyAsyncEndPoint.java147
-rw-r--r--jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/FlowControlStrategyFactory.java37
-rw-r--r--jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYAsyncConnection.java232
-rw-r--r--jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYClient.java182
-rw-r--r--jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYServerConnector.java147
-rw-r--r--jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/ServerSPDYAsyncConnectionFactory.java67
-rw-r--r--jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/AbstractTest.java43
-rw-r--r--jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ClosedStreamTest.java82
-rw-r--r--jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/FlowControlTest.java165
-rw-r--r--jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/GoAwayTest.java31
-rw-r--r--jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/HeadersTest.java28
-rw-r--r--jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/IdleTimeoutTest.java44
-rw-r--r--jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/PingTest.java32
-rw-r--r--jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ProtocolViolationsTest.java36
-rw-r--r--jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/PushStreamTest.java235
-rw-r--r--jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ResetStreamTest.java23
-rw-r--r--jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SPDYClientFactoryTest.java28
-rw-r--r--jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SPDYServerConnectorTest.java28
-rw-r--r--jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SSLEngineLeakTest.java13
-rw-r--r--jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SSLSynReplyTest.java28
-rw-r--r--jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SettingsTest.java28
-rw-r--r--jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SynDataReplyDataLoadTest.java32
-rw-r--r--jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SynReplyTest.java33
-rw-r--r--jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/UnsupportedVersionTest.java16
163 files changed, 9548 insertions, 2895 deletions
diff --git a/jetty-spdy/pom.xml b/jetty-spdy/pom.xml
index f89df26..bacc02b 100644
--- a/jetty-spdy/pom.xml
+++ b/jetty-spdy/pom.xml
@@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>9.0.0-SNAPSHOT</version>
+ <version>8.1.6-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -13,7 +13,7 @@
<name>Jetty :: SPDY :: Parent</name>
<properties>
- <npn.version>1.0.0.v20120402</npn.version>
+ <npn.version>1.1.0.v20120525</npn.version>
</properties>
<modules>
diff --git a/jetty-spdy/spdy-core/pom.xml b/jetty-spdy/spdy-core/pom.xml
index 00b04b0..14e6d53 100644
--- a/jetty-spdy/spdy-core/pom.xml
+++ b/jetty-spdy/spdy-core/pom.xml
@@ -3,12 +3,12 @@
<parent>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-parent</artifactId>
- <version>9.0.0-SNAPSHOT</version>
+ <version>8.1.6-SNAPSHOT</version>
</parent>
- <modelVersion>4.0.0</modelVersion>
- <artifactId>spdy-core</artifactId>
- <name>Jetty :: SPDY :: Core</name>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>spdy-core</artifactId>
+ <name>Jetty :: SPDY :: Core</name>
<dependencies>
<dependency>
@@ -16,26 +16,21 @@
<artifactId>jetty-util</artifactId>
<version>${project.version}</version>
</dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest-all</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <scope>test</scope>
- </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/ByteBufferPool.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/ByteBufferPool.java
new file mode 100644
index 0000000..e2964cc
--- /dev/null
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/ByteBufferPool.java
@@ -0,0 +1,46 @@
+//========================================================================
+//Copyright 2012 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.spdy;
+
+import java.nio.ByteBuffer;
+
+/**
+ * <p>A {@link ByteBuffer} pool.</p>
+ * <p>Acquired buffers may be {@link #release(ByteBuffer) released} but they do not need to;
+ * if they are released, they may be recycled and reused, otherwise they will be garbage
+ * collected as usual.</p>
+ */
+public interface ByteBufferPool
+{
+ /**
+ * <p>Requests a {@link ByteBuffer} of the given size.</p>
+ * <p>The returned buffer may have a bigger capacity than the size being
+ * requested but it will have the limit set to the given size.</p>
+ *
+ * @param size the size of the buffer
+ * @param direct whether the buffer must be direct or not
+ * @return the requested buffer
+ * @see #release(ByteBuffer)
+ */
+ public ByteBuffer acquire(int size, boolean direct);
+
+ /**
+ * <p>Returns a {@link ByteBuffer}, usually obtained with {@link #acquire(int, boolean)}
+ * (but not necessarily), making it available for recycling and reuse.</p>
+ *
+ * @param buffer the buffer to return
+ * @see #acquire(int, boolean)
+ */
+ public void release(ByteBuffer buffer);
+}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/CompressionDictionary.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/CompressionDictionary.java
index 0aa024d..8b8b83a 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/CompressionDictionary.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/CompressionDictionary.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/CompressionFactory.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/CompressionFactory.java
index f81a5b3..620c24a 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/CompressionFactory.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/CompressionFactory.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Controller.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Controller.java
index f86ed12..75e26ef 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Controller.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Controller.java
@@ -1,28 +1,25 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.spdy.api.Handler;
public interface Controller<T>
{
- public int write(ByteBuffer buffer, Callback<T> callback, T context);
+ public int write(ByteBuffer buffer, Handler<T> handler, T context);
public void close(boolean onlyOutput);
}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/FlowControlStrategy.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/FlowControlStrategy.java
new file mode 100644
index 0000000..c08bc42
--- /dev/null
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/FlowControlStrategy.java
@@ -0,0 +1,86 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy;
+
+import org.eclipse.jetty.spdy.api.DataInfo;
+
+// TODO: add methods that tell how much written and whether we're TCP congested ?
+public interface FlowControlStrategy
+{
+ public int getWindowSize(ISession session);
+
+ public void setWindowSize(ISession session, int windowSize);
+
+ public void onNewStream(ISession session, IStream stream);
+
+ public void onWindowUpdate(ISession session, IStream stream, int delta);
+
+ public void updateWindow(ISession session, IStream stream, int delta);
+
+ public void onDataReceived(ISession session, IStream stream, DataInfo dataInfo);
+
+ public void onDataConsumed(ISession session, IStream stream, DataInfo dataInfo, int delta);
+
+ public static class None implements FlowControlStrategy
+ {
+ private volatile int windowSize;
+
+ public None()
+ {
+ this(65536);
+ }
+
+ public None(int windowSize)
+ {
+ this.windowSize = windowSize;
+ }
+
+ @Override
+ public int getWindowSize(ISession session)
+ {
+ return windowSize;
+ }
+
+ @Override
+ public void setWindowSize(ISession session, int windowSize)
+ {
+ this.windowSize = windowSize;
+ }
+
+ @Override
+ public void onNewStream(ISession session, IStream stream)
+ {
+ stream.updateWindowSize(windowSize);
+ }
+
+ @Override
+ public void onWindowUpdate(ISession session, IStream stream, int delta)
+ {
+ }
+
+ @Override
+ public void updateWindow(ISession session, IStream stream, int delta)
+ {
+ }
+
+ @Override
+ public void onDataReceived(ISession session, IStream stream, DataInfo dataInfo)
+ {
+ }
+
+ @Override
+ public void onDataConsumed(ISession session, IStream stream, DataInfo dataInfo, int delta)
+ {
+ }
+ }
+}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/ISession.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/ISession.java
index b00d885..41bbfd7 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/ISession.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/ISession.java
@@ -1,27 +1,24 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.spdy.api.DataInfo;
+import org.eclipse.jetty.spdy.api.Handler;
import org.eclipse.jetty.spdy.api.Session;
import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.util.Callback;
public interface ISession extends Session
{
@@ -32,7 +29,7 @@ public interface ISession extends Session
*/
public void flush();
- public <C> void control(IStream stream, ControlFrame frame, long timeout, TimeUnit unit, Callback<C> callback, C context);
+ public <C> void control(IStream stream, ControlFrame frame, long timeout, TimeUnit unit, Handler<C> handler, C context);
- public <C> void data(IStream stream, DataInfo dataInfo, long timeout, TimeUnit unit, Callback<C> callback, C context);
+ public <C> void data(IStream stream, DataInfo dataInfo, long timeout, TimeUnit unit, Handler<C> handler, C context);
}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IStream.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IStream.java
index 07934c1..ad75f40 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IStream.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IStream.java
@@ -1,29 +1,24 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy;
-import java.nio.ByteBuffer;
-
+import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.SessionFrameListener;
import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.api.StreamFrameListener;
import org.eclipse.jetty.spdy.api.SynInfo;
import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.DataFrame;
/**
* <p>The internal interface that represents a stream.</p>
@@ -77,37 +72,36 @@ public interface IStream extends Stream
* for example by updating the stream's state or by calling listeners.</p>
*
* @param frame the control frame to process
- * @see #process(DataFrame, ByteBuffer)
+ * @see #process(DataInfo)
*/
public void process(ControlFrame frame);
/**
- * <p>Processes the given data frame along with the given byte buffer,
+ * <p>Processes the given {@code dataInfo},
* for example by updating the stream's state or by calling listeners.</p>
*
- * @param frame the data frame to process
- * @param data the byte buffer to process
+ * @param dataInfo the DataInfo to process
* @see #process(ControlFrame)
*/
- public void process(DataFrame frame, ByteBuffer data);
-
+ public void process(DataInfo dataInfo);
+
/**
* <p>Associate the given {@link IStream} to this {@link IStream}.</p>
- *
+ *
* @param stream the stream to associate with this stream
*/
public void associate(IStream stream);
-
+
/**
* <p>remove the given associated {@link IStream} from this stream</p>
- *
+ *
* @param stream the stream to be removed
*/
public void disassociate(IStream stream);
-
+
/**
* <p>Overrides Stream.getAssociatedStream() to return an instance of IStream instead of Stream
- *
+ *
* @see Stream#getAssociatedStream()
*/
@Override
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IdleListener.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IdleListener.java
index 8ca26ad..42e4449 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IdleListener.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IdleListener.java
@@ -1,19 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
+///========================================================================
+//Copyright 2011-2012 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.spdy;
public interface IdleListener
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Promise.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Promise.java
index acaa104..b08cde8 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Promise.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Promise.java
@@ -1,28 +1,26 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy;
+import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.spdy.api.Handler;
/**
* <p>A {@link Promise} is a {@link Future} that allows a result or a failure to be set,
@@ -30,7 +28,7 @@ import org.eclipse.jetty.util.Callback;
*
* @param <T> the type of the result object
*/
-public class Promise<T> implements Callback<T>, Future<T>
+public class Promise<T> implements Handler<T>, Future<T>
{
private final CountDownLatch latch = new CountDownLatch(1);
private boolean cancelled;
@@ -38,16 +36,15 @@ public class Promise<T> implements Callback<T>, Future<T>
private T promise;
@Override
- public void completed(T context)
+ public void completed(T result)
{
- this.promise = context;
+ this.promise = result;
latch.countDown();
}
@Override
public void failed(T context, Throwable x)
{
- this.promise = context;
this.failure = x;
latch.countDown();
}
@@ -90,6 +87,8 @@ public class Promise<T> implements Callback<T>, Future<T>
private T result() throws ExecutionException
{
+ if (isCancelled())
+ throw new CancellationException();
Throwable failure = this.failure;
if (failure != null)
throw new ExecutionException(failure);
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/PushSynInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/PushSynInfo.java
index a460d54..8befef4 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/PushSynInfo.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/PushSynInfo.java
@@ -1,4 +1,16 @@
package org.eclipse.jetty.spdy;
+//========================================================================
+//Copyright 2011-2012 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.
+//========================================================================
import org.eclipse.jetty.spdy.api.SynInfo;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/SPDYv3FlowControlStrategy.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/SPDYv3FlowControlStrategy.java
new file mode 100644
index 0000000..8eb4b0e
--- /dev/null
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/SPDYv3FlowControlStrategy.java
@@ -0,0 +1,84 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy;
+
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.spdy.api.DataInfo;
+import org.eclipse.jetty.spdy.api.Stream;
+import org.eclipse.jetty.spdy.frames.WindowUpdateFrame;
+
+public class SPDYv3FlowControlStrategy implements FlowControlStrategy
+{
+ private volatile int windowSize;
+
+ @Override
+ public int getWindowSize(ISession session)
+ {
+ return windowSize;
+ }
+
+ @Override
+ public void setWindowSize(ISession session, int windowSize)
+ {
+ int prevWindowSize = this.windowSize;
+ this.windowSize = windowSize;
+ for (Stream stream : session.getStreams())
+ ((IStream)stream).updateWindowSize(windowSize - prevWindowSize);
+ }
+
+ @Override
+ public void onNewStream(ISession session, IStream stream)
+ {
+ stream.updateWindowSize(windowSize);
+ }
+
+ @Override
+ public void onWindowUpdate(ISession session, IStream stream, int delta)
+ {
+ if (stream != null)
+ stream.updateWindowSize(delta);
+ }
+
+ @Override
+ public void updateWindow(ISession session, IStream stream, int delta)
+ {
+ stream.updateWindowSize(delta);
+ }
+
+ @Override
+ public void onDataReceived(ISession session, IStream stream, DataInfo dataInfo)
+ {
+ // Do nothing
+ }
+
+ @Override
+ public void onDataConsumed(ISession session, IStream stream, DataInfo dataInfo, int delta)
+ {
+ // This is the algorithm for flow control.
+ // This method may be called multiple times with delta=1, but we only send a window
+ // update when the whole dataInfo has been consumed.
+ // Other policies may be to send window updates when consumed() is greater than
+ // a certain threshold, etc. but for now the policy is not pluggable for simplicity.
+ // Note that the frequency of window updates depends on the read buffer, that
+ // should not be too smaller than the window size to avoid frequent window updates.
+ // Therefore, a pluggable policy should be able to modify the read buffer capacity.
+ int length = dataInfo.length();
+ if (dataInfo.consumed() == length && !stream.isClosed() && length > 0)
+ {
+ WindowUpdateFrame windowUpdateFrame = new WindowUpdateFrame(session.getVersion(), stream.getId(), length);
+ session.control(stream, windowUpdateFrame, 0, TimeUnit.MILLISECONDS, null, null);
+ }
+ }
+}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/SessionException.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/SessionException.java
index 3e0c195..65f3754 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/SessionException.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/SessionException.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy;
@@ -20,7 +17,6 @@ import org.eclipse.jetty.spdy.api.SessionStatus;
public class SessionException extends RuntimeException
{
-
private final SessionStatus sessionStatus;
public SessionException(SessionStatus sessionStatus)
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardByteBufferPool.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardByteBufferPool.java
new file mode 100644
index 0000000..60b6190
--- /dev/null
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardByteBufferPool.java
@@ -0,0 +1,97 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy;
+
+import java.nio.ByteBuffer;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ConcurrentMap;
+
+public class StandardByteBufferPool implements ByteBufferPool
+{
+ private final ConcurrentMap<Integer, Queue<ByteBuffer>> directBuffers = new ConcurrentHashMap<>();
+ private final ConcurrentMap<Integer, Queue<ByteBuffer>> heapBuffers = new ConcurrentHashMap<>();
+ private final int factor;
+
+ public StandardByteBufferPool()
+ {
+ this(1024);
+ }
+
+ public StandardByteBufferPool(int factor)
+ {
+ this.factor = factor;
+ }
+
+ public ByteBuffer acquire(int size, boolean direct)
+ {
+ int bucket = bucketFor(size);
+ ConcurrentMap<Integer, Queue<ByteBuffer>> buffers = buffersFor(direct);
+
+ ByteBuffer result = null;
+ Queue<ByteBuffer> byteBuffers = buffers.get(bucket);
+ if (byteBuffers != null)
+ result = byteBuffers.poll();
+
+ if (result == null)
+ {
+ int capacity = bucket * factor;
+ result = direct ? ByteBuffer.allocateDirect(capacity) : ByteBuffer.allocate(capacity);
+ }
+
+ result.clear();
+ result.limit(size);
+
+ return result;
+ }
+
+ public void release(ByteBuffer buffer)
+ {
+ int bucket = bucketFor(buffer.capacity());
+ ConcurrentMap<Integer, Queue<ByteBuffer>> buffers = buffersFor(buffer.isDirect());
+
+ // Avoid to create a new queue every time, just to be discarded immediately
+ Queue<ByteBuffer> byteBuffers = buffers.get(bucket);
+ if (byteBuffers == null)
+ {
+ byteBuffers = new ConcurrentLinkedQueue<>();
+ Queue<ByteBuffer> existing = buffers.putIfAbsent(bucket, byteBuffers);
+ if (existing != null)
+ byteBuffers = existing;
+ }
+
+ buffer.clear();
+ byteBuffers.offer(buffer);
+ }
+
+ public void clear()
+ {
+ directBuffers.clear();
+ heapBuffers.clear();
+ }
+
+ private int bucketFor(int size)
+ {
+ int bucket = size / factor;
+ if (size % factor > 0)
+ ++bucket;
+ return bucket;
+ }
+
+ private ConcurrentMap<Integer, Queue<ByteBuffer>> buffersFor(boolean direct)
+ {
+ return direct ? directBuffers : heapBuffers;
+ }
+}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardCompressionFactory.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardCompressionFactory.java
index f0a7eeb..1e7e6e0 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardCompressionFactory.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardCompressionFactory.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java
index 8c645a9..d5524e2 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java
@@ -1,26 +1,27 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.InterruptedByTimeoutException;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -33,9 +34,10 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
-import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.GoAwayInfo;
+import org.eclipse.jetty.spdy.api.Handler;
import org.eclipse.jetty.spdy.api.PingInfo;
import org.eclipse.jetty.spdy.api.RstInfo;
import org.eclipse.jetty.spdy.api.SPDYException;
@@ -50,6 +52,7 @@ import org.eclipse.jetty.spdy.api.StreamStatus;
import org.eclipse.jetty.spdy.api.SynInfo;
import org.eclipse.jetty.spdy.frames.ControlFrame;
import org.eclipse.jetty.spdy.frames.ControlFrameType;
+import org.eclipse.jetty.spdy.frames.CredentialFrame;
import org.eclipse.jetty.spdy.frames.DataFrame;
import org.eclipse.jetty.spdy.frames.GoAwayFrame;
import org.eclipse.jetty.spdy.frames.HeadersFrame;
@@ -61,11 +64,13 @@ import org.eclipse.jetty.spdy.frames.SynStreamFrame;
import org.eclipse.jetty.spdy.frames.WindowUpdateFrame;
import org.eclipse.jetty.spdy.generator.Generator;
import org.eclipse.jetty.spdy.parser.Parser;
-import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Atomics;
+import org.eclipse.jetty.util.component.AggregateLifeCycle;
+import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
-public class StandardSession implements ISession, Parser.Listener, Callback<StandardSession.FrameBytes>
+public class StandardSession implements ISession, Parser.Listener, Handler<StandardSession.FrameBytes>, Dumpable
{
private static final Logger logger = Log.getLogger(Session.class);
private static final ThreadLocal<Integer> handlerInvocations = new ThreadLocal<Integer>()
@@ -77,6 +82,7 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
}
};
+ private final Map<String, Object> attributes = new ConcurrentHashMap<>();
private final List<Listener> listeners = new CopyOnWriteArrayList<>();
private final ConcurrentMap<Integer, IStream> streams = new ConcurrentHashMap<>();
private final LinkedList<FrameBytes> queue = new LinkedList<>();
@@ -93,11 +99,13 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
private final AtomicBoolean goAwaySent = new AtomicBoolean();
private final AtomicBoolean goAwayReceived = new AtomicBoolean();
private final AtomicInteger lastStreamId = new AtomicInteger();
+ private final FlowControlStrategy flowControlStrategy;
private boolean flushing;
- private volatile int windowSize = 65536;
+ private Throwable failure;
public StandardSession(short version, ByteBufferPool bufferPool, Executor threadPool, ScheduledExecutorService scheduler,
- Controller<FrameBytes> controller, IdleListener idleListener, int initialStreamId, SessionFrameListener listener, Generator generator)
+ Controller<FrameBytes> controller, IdleListener idleListener, int initialStreamId, SessionFrameListener listener,
+ Generator generator, FlowControlStrategy flowControlStrategy)
{
this.version = version;
this.bufferPool = bufferPool;
@@ -109,6 +117,7 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
this.pingIds = new AtomicInteger(initialStreamId);
this.listener = listener;
this.generator = generator;
+ this.flowControlStrategy = flowControlStrategy;
}
@Override
@@ -138,7 +147,7 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
}
@Override
- public void syn(SynInfo synInfo, StreamFrameListener listener, long timeout, TimeUnit unit, Callback<Stream> callback)
+ public void syn(SynInfo synInfo, StreamFrameListener listener, long timeout, TimeUnit unit, Handler<Stream> handler)
{
// Synchronization is necessary.
// SPEC v3, 2.3.1 requires that the stream creation be monotonically crescent
@@ -153,10 +162,12 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
synchronized (this)
{
int streamId = streamIds.getAndAdd(2);
- SynStreamFrame synStream = new SynStreamFrame(version, synInfo.getFlags(), streamId, associatedStreamId, synInfo.getPriority(), synInfo.getHeaders());
+ // TODO: for SPDYv3 we need to support the "slot" argument
+ SynStreamFrame synStream = new SynStreamFrame(version, synInfo.getFlags(), streamId, associatedStreamId, synInfo.getPriority(), (short)0, synInfo.getHeaders());
IStream stream = createStream(synStream, listener, true);
- control(stream, synStream, timeout, unit, callback, stream);
+ generateAndEnqueueControlFrame(stream, synStream, timeout, unit, handler, stream);
}
+ flush();
}
@Override
@@ -168,19 +179,19 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
}
@Override
- public void rst(RstInfo rstInfo, long timeout, TimeUnit unit, Callback<Void> callback)
+ public void rst(RstInfo rstInfo, long timeout, TimeUnit unit, Handler<Void> handler)
{
// SPEC v3, 2.2.2
if (goAwaySent.get())
{
- complete(callback,null);
+ complete(handler,null);
}
else
{
int streamId = rstInfo.getStreamId();
IStream stream = streams.get(streamId);
RstStreamFrame frame = new RstStreamFrame(version,streamId,rstInfo.getStreamStatus().getCode(version));
- control(stream,frame,timeout,unit, callback,null);
+ control(stream,frame,timeout,unit,handler,null);
if (stream != null)
{
stream.process(frame);
@@ -198,10 +209,10 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
}
@Override
- public void settings(SettingsInfo settingsInfo, long timeout, TimeUnit unit, Callback<Void> callback)
+ public void settings(SettingsInfo settingsInfo, long timeout, TimeUnit unit, Handler<Void> handler)
{
SettingsFrame frame = new SettingsFrame(version,settingsInfo.getFlags(),settingsInfo.getSettings());
- control(null,frame,timeout,unit, callback,null);
+ control(null, frame, timeout, unit, handler, null);
}
@Override
@@ -213,12 +224,12 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
}
@Override
- public void ping(long timeout, TimeUnit unit, Callback<PingInfo> callback)
+ public void ping(long timeout, TimeUnit unit, Handler<PingInfo> handler)
{
int pingId = pingIds.getAndAdd(2);
PingInfo pingInfo = new PingInfo(pingId);
PingFrame frame = new PingFrame(version,pingId);
- control(null,frame,timeout,unit, callback,pingInfo);
+ control(null,frame,timeout,unit,handler,pingInfo);
}
@Override
@@ -235,23 +246,23 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
}
@Override
- public void goAway(long timeout, TimeUnit unit, Callback<Void> callback)
+ public void goAway(long timeout, TimeUnit unit, Handler<Void> handler)
{
- goAway(SessionStatus.OK,timeout,unit, callback);
+ goAway(SessionStatus.OK, timeout, unit, handler);
}
- private void goAway(SessionStatus sessionStatus, long timeout, TimeUnit unit, Callback<Void> callback)
+ private void goAway(SessionStatus sessionStatus, long timeout, TimeUnit unit, Handler<Void> handler)
{
if (goAwaySent.compareAndSet(false,true))
{
if (!goAwayReceived.get())
{
GoAwayFrame frame = new GoAwayFrame(version,lastStreamId.get(),sessionStatus.getCode());
- control(null,frame,timeout,unit, callback,null);
+ control(null,frame,timeout,unit,handler,null);
return;
}
}
- complete(callback, null);
+ complete(handler, null);
}
@Override
@@ -263,16 +274,40 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
}
@Override
+ public IStream getStream(int streamId)
+ {
+ return streams.get(streamId);
+ }
+
+ @Override
+ public Object getAttribute(String key)
+ {
+ return attributes.get(key);
+ }
+
+ @Override
+ public void setAttribute(String key, Object value)
+ {
+ attributes.put(key, value);
+ }
+
+ @Override
+ public Object removeAttribute(String key)
+ {
+ return attributes.remove(key);
+ }
+
+ @Override
public void onControlFrame(ControlFrame frame)
{
- notifyIdle(idleListener,false);
+ notifyIdle(idleListener, false);
try
{
- logger.debug("Processing {}",frame);
+ logger.debug("Processing {}", frame);
if (goAwaySent.get())
{
- logger.debug("Skipped processing of {}",frame);
+ logger.debug("Skipped processing of {}", frame);
return;
}
@@ -323,6 +358,11 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
onWindowUpdate((WindowUpdateFrame)frame);
break;
}
+ case CREDENTIAL:
+ {
+ onCredential((CredentialFrame)frame);
+ break;
+ }
default:
{
throw new IllegalStateException();
@@ -331,7 +371,7 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
}
finally
{
- notifyIdle(idleListener,true);
+ notifyIdle(idleListener, true);
}
}
@@ -341,11 +381,11 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
notifyIdle(idleListener, false);
try
{
- logger.debug("Processing {}, {} data bytes",frame,data.remaining());
+ logger.debug("Processing {}, {} data bytes", frame, data.remaining());
if (goAwaySent.get())
{
- logger.debug("Skipped processing of {}",frame);
+ logger.debug("Skipped processing of {}", frame);
return;
}
@@ -353,18 +393,18 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
IStream stream = streams.get(streamId);
if (stream == null)
{
- RstInfo rstInfo = new RstInfo(streamId,StreamStatus.INVALID_STREAM);
- logger.debug("Unknown stream {}",rstInfo);
+ RstInfo rstInfo = new RstInfo(streamId, StreamStatus.INVALID_STREAM);
+ logger.debug("Unknown stream {}", rstInfo);
rst(rstInfo);
}
else
{
- processData(stream,frame,data);
+ processData(stream, frame, data);
}
}
finally
{
- notifyIdle(idleListener,true);
+ notifyIdle(idleListener, true);
}
}
@@ -374,10 +414,19 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
listener.onIdle(idle);
}
- private void processData(IStream stream, DataFrame frame, ByteBuffer data)
+ private void processData(final IStream stream, DataFrame frame, ByteBuffer data)
{
- stream.process(frame,data);
- updateLastStreamId(stream);
+ ByteBufferDataInfo dataInfo = new ByteBufferDataInfo(data, frame.isClose(), frame.isCompress())
+ {
+ @Override
+ public void consume(int delta)
+ {
+ super.consume(delta);
+ flowControlStrategy.onDataConsumed(StandardSession.this, stream, this, delta);
+ }
+ };
+ flowControlStrategy.onDataReceived(this, stream, dataInfo);
+ stream.process(dataInfo);
if (stream.isClosed())
removeStream(stream);
}
@@ -407,6 +456,8 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
private void processSyn(SessionFrameListener listener, IStream stream, SynStreamFrame frame)
{
stream.process(frame);
+ // Update the last stream id before calling the application (which may send a GO_AWAY)
+ updateLastStreamId(stream);
SynInfo synInfo = new SynInfo(frame.getHeaders(),frame.isClose(),frame.getPriority());
StreamFrameListener streamListener = notifyOnSyn(listener,stream,synInfo);
stream.setStreamFrameListener(streamListener);
@@ -452,7 +503,9 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
private IStream newStream(SynStreamFrame frame)
{
IStream associatedStream = streams.get(frame.getAssociatedStreamId());
- return new StandardStream(frame, this, windowSize, associatedStream);
+ IStream stream = new StandardStream(frame.getStreamId(), frame.getPriority(), this, associatedStream);
+ flowControlStrategy.onNewStream(this, stream);
+ return stream;
}
private void notifyStreamCreated(IStream stream)
@@ -467,7 +520,12 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
}
catch (Exception x)
{
- logger.info("Exception while notifying listener " + listener,x);
+ logger.info("Exception while notifying listener " + listener, x);
+ }
+ catch (Error x)
+ {
+ logger.info("Exception while notifying listener " + listener, x);
+ throw x;
}
}
}
@@ -498,7 +556,12 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
}
catch (Exception x)
{
- logger.info("Exception while notifying listener " + listener,x);
+ logger.info("Exception while notifying listener " + listener, x);
+ }
+ catch (Error x)
+ {
+ logger.info("Exception while notifying listener " + listener, x);
+ throw x;
}
}
}
@@ -547,15 +610,12 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
Settings.Setting windowSizeSetting = frame.getSettings().get(Settings.ID.INITIAL_WINDOW_SIZE);
if (windowSizeSetting != null)
{
- int prevWindowSize = windowSize;
- windowSize = windowSizeSetting.value();
- for (IStream stream : streams.values())
- stream.updateWindowSize(windowSize - prevWindowSize);
- logger.debug("Updated window size to {}",windowSize);
+ int windowSize = windowSizeSetting.value();
+ setWindowSize(windowSize);
+ logger.debug("Updated session window size to {}", windowSize);
}
-
- SettingsInfo settingsInfo = new SettingsInfo(frame.getSettings(),frame.isClearPersisted());
- notifyOnSettings(listener,settingsInfo);
+ SettingsInfo settingsInfo = new SettingsInfo(frame.getSettings(), frame.isClearPersisted());
+ notifyOnSettings(listener, settingsInfo);
flush();
}
@@ -615,8 +675,14 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
{
int streamId = frame.getStreamId();
IStream stream = streams.get(streamId);
- if (stream != null)
- stream.process(frame);
+ flowControlStrategy.onWindowUpdate(this, stream, frame.getWindowDelta());
+ flush();
+ }
+
+ private void onCredential(CredentialFrame frame)
+ {
+ logger.warn("{} frame not yet supported", ControlFrameType.CREDENTIAL);
+ flush();
}
protected void close()
@@ -638,7 +704,12 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
}
catch (Exception xx)
{
- logger.info("Exception while notifying listener " + listener,xx);
+ logger.info("Exception while notifying listener " + listener, xx);
+ }
+ catch (Error xx)
+ {
+ logger.info("Exception while notifying listener " + listener, xx);
+ throw xx;
}
}
@@ -646,17 +717,21 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
{
try
{
- if (listener != null)
- {
- logger.debug("Invoking callback with {} on listener {}",synInfo,listener);
- return listener.onSyn(stream,synInfo);
- }
+ if (listener == null)
+ return null;
+ logger.debug("Invoking callback with {} on listener {}",synInfo,listener);
+ return listener.onSyn(stream,synInfo);
}
catch (Exception x)
{
logger.info("Exception while notifying listener " + listener,x);
+ return null;
+ }
+ catch (Error x)
+ {
+ logger.info("Exception while notifying listener " + listener, x);
+ throw x;
}
- return null;
}
private void notifyOnRst(SessionFrameListener listener, RstInfo rstInfo)
@@ -671,7 +746,12 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
}
catch (Exception x)
{
- logger.info("Exception while notifying listener " + listener,x);
+ logger.info("Exception while notifying listener " + listener, x);
+ }
+ catch (Error x)
+ {
+ logger.info("Exception while notifying listener " + listener, x);
+ throw x;
}
}
@@ -687,7 +767,12 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
}
catch (Exception x)
{
- logger.info("Exception while notifying listener " + listener,x);
+ logger.info("Exception while notifying listener " + listener, x);
+ }
+ catch (Error x)
+ {
+ logger.info("Exception while notifying listener " + listener, x);
+ throw x;
}
}
@@ -703,7 +788,12 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
}
catch (Exception x)
{
- logger.info("Exception while notifying listener " + listener,x);
+ logger.info("Exception while notifying listener " + listener, x);
+ }
+ catch (Error x)
+ {
+ logger.info("Exception while notifying listener " + listener, x);
+ throw x;
}
}
@@ -719,32 +809,36 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
}
catch (Exception x)
{
- logger.info("Exception while notifying listener " + listener,x);
+ logger.info("Exception while notifying listener " + listener, x);
+ }
+ catch (Error x)
+ {
+ logger.info("Exception while notifying listener " + listener, x);
+ throw x;
}
}
@Override
- public <C> void control(IStream stream, ControlFrame frame, long timeout, TimeUnit unit, Callback<C> callback, C context)
+ public <C> void control(IStream stream, ControlFrame frame, long timeout, TimeUnit unit, Handler<C> handler, C context)
+ {
+ generateAndEnqueueControlFrame(stream,frame,timeout,unit,handler,context);
+ flush();
+ }
+
+ private <C> void generateAndEnqueueControlFrame(IStream stream, ControlFrame frame, long timeout, TimeUnit unit, Handler<C> handler, C context)
{
try
{
- if (stream != null)
- {
- updateLastStreamId(stream);
- if (stream.isClosed())
- removeStream(stream);
- }
-
// Synchronization is necessary, since we may have concurrent replies
// and those needs to be generated and enqueued atomically in order
// to maintain a correct compression context
synchronized (this)
{
ByteBuffer buffer = generator.control(frame);
- logger.debug("Queuing {} on {}",frame,stream);
- ControlFrameBytes<C> frameBytes = new ControlFrameBytes<>(stream, callback,context,frame,buffer);
+ logger.debug("Queuing {} on {}", frame, stream);
+ ControlFrameBytes<C> frameBytes = new ControlFrameBytes<>(stream, handler, context, frame, buffer);
if (timeout > 0)
- frameBytes.task = scheduler.schedule(frameBytes,timeout,unit);
+ frameBytes.task = scheduler.schedule(frameBytes, timeout, unit);
// Special handling for PING frames, they must be sent as soon as possible
if (ControlFrameType.PING == frame.getType())
@@ -752,40 +846,27 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
else
append(frameBytes);
}
-
- flush();
}
- catch (Throwable x)
+ catch (Exception x)
{
- notifyHandlerFailed(callback, context, x);
+ notifyHandlerFailed(handler, context, x);
}
}
private void updateLastStreamId(IStream stream)
{
int streamId = stream.getId();
- if (stream.isClosed() && streamId % 2 != streamIds.get() % 2)
- {
- // Non-blocking atomic update
- int oldValue = lastStreamId.get();
- while (streamId > oldValue)
- {
- if (lastStreamId.compareAndSet(oldValue,streamId))
- break;
- oldValue = lastStreamId.get();
- }
- }
+ if (streamId % 2 != streamIds.get() % 2)
+ Atomics.updateMax(lastStreamId, streamId);
}
@Override
- public <C> void data(IStream stream, DataInfo dataInfo, long timeout, TimeUnit unit, Callback<C> callback, C context)
+ public <C> void data(IStream stream, DataInfo dataInfo, long timeout, TimeUnit unit, Handler<C> handler, C context)
{
logger.debug("Queuing {} on {}",dataInfo,stream);
- DataFrameBytes<C> frameBytes = new DataFrameBytes<>(stream, callback,context,dataInfo);
+ DataFrameBytes<C> frameBytes = new DataFrameBytes<>(stream,handler,context,dataInfo);
if (timeout > 0)
- {
frameBytes.task = scheduler.schedule(frameBytes,timeout,unit);
- }
append(frameBytes);
flush();
}
@@ -818,9 +899,11 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
if (buffer != null)
{
queue.remove(i);
- // TODO: stream.isUniDirectional() check here is only needed for pushStreams which send a syn with close=true --> find a better solution
- if (stream != null && !streams.containsValue(stream) && !stream.isUnidirectional())
+ if (stream != null && stream.isReset())
+ {
frameBytes.fail(new StreamException(stream.getId(),StreamStatus.INVALID_STREAM));
+ return;
+ }
break;
}
@@ -843,34 +926,50 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
private void append(FrameBytes frameBytes)
{
+ Throwable failure;
synchronized (queue)
{
- int index = queue.size();
- while (index > 0)
+ failure = this.failure;
+ if (failure == null)
{
- FrameBytes element = queue.get(index - 1);
- if (element.compareTo(frameBytes) >= 0)
- break;
- --index;
+ int index = queue.size();
+ while (index > 0)
+ {
+ FrameBytes element = queue.get(index - 1);
+ if (element.compareTo(frameBytes) >= 0)
+ break;
+ --index;
+ }
+ queue.add(index,frameBytes);
}
- queue.add(index,frameBytes);
}
+
+ if (failure != null)
+ frameBytes.fail(new SPDYException(failure));
}
private void prepend(FrameBytes frameBytes)
{
+ Throwable failure;
synchronized (queue)
{
- int index = 0;
- while (index < queue.size())
+ failure = this.failure;
+ if (failure == null)
{
- FrameBytes element = queue.get(index);
- if (element.compareTo(frameBytes) <= 0)
- break;
- ++index;
+ int index = 0;
+ while (index < queue.size())
+ {
+ FrameBytes element = queue.get(index);
+ if (element.compareTo(frameBytes) <= 0)
+ break;
+ ++index;
+ }
+ queue.add(index,frameBytes);
}
- queue.add(index,frameBytes);
}
+
+ if (failure != null)
+ frameBytes.fail(new SPDYException(failure));
}
@Override
@@ -885,21 +984,35 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
}
@Override
- public void failed(FrameBytes context, Throwable x)
+ public void failed(FrameBytes frameBytes, Throwable x)
{
- throw new SPDYException(x);
+ List<FrameBytes> frameBytesToFail = new ArrayList<>();
+ frameBytesToFail.add(frameBytes);
+
+ synchronized (queue)
+ {
+ failure = x;
+ String logMessage = String.format("Failed write of %s, failing all %d frame(s) in queue",frameBytes,queue.size());
+ logger.debug(logMessage,x);
+ frameBytesToFail.addAll(queue);
+ queue.clear();
+ flushing = false;
+ }
+
+ for (FrameBytes fb : frameBytesToFail)
+ fb.fail(x);
}
- protected void write(ByteBuffer buffer, Callback<FrameBytes> callback, FrameBytes frameBytes)
+ protected void write(ByteBuffer buffer, Handler<FrameBytes> handler, FrameBytes frameBytes)
{
if (controller != null)
{
logger.debug("Writing {} frame bytes of {}",buffer.remaining(),frameBytes);
- controller.write(buffer, callback,frameBytes);
+ controller.write(buffer,handler,frameBytes);
}
}
- private <C> void complete(final Callback<C> callback, final C context)
+ private <C> void complete(final Handler<C> handler, final C context)
{
// Applications may send and queue up a lot of frames and
// if we call Handler.completed() only synchronously we risk
@@ -913,8 +1026,8 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
@Override
public void run()
{
- if (callback != null)
- notifyHandlerCompleted(callback,context);
+ if (handler != null)
+ notifyHandlerCompleted(handler,context);
flush();
}
});
@@ -924,8 +1037,8 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
handlerInvocations.set(invocations + 1);
try
{
- if (callback != null)
- notifyHandlerCompleted(callback,context);
+ if (handler != null)
+ notifyHandlerCompleted(handler,context);
flush();
}
finally
@@ -935,31 +1048,72 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
}
}
- private <C> void notifyHandlerCompleted(Callback<C> callback, C context)
+ private <C> void notifyHandlerCompleted(Handler<C> handler, C context)
{
try
{
- callback.completed(context);
+ handler.completed(context);
}
catch (Exception x)
{
- logger.info("Exception while notifying callback " + callback,x);
+ logger.info("Exception while notifying handler " + handler, x);
+ }
+ catch (Error x)
+ {
+ logger.info("Exception while notifying handler " + handler, x);
+ throw x;
}
}
- private <C> void notifyHandlerFailed(Callback<C> callback, C context, Throwable x)
+ private <C> void notifyHandlerFailed(Handler<C> handler, C context, Throwable x)
{
try
{
- if (callback != null)
- callback.failed(context, x);
+ if (handler != null)
+ handler.failed(context, x);
}
catch (Exception xx)
{
- logger.info("Exception while notifying callback " + callback,xx);
+ logger.info("Exception while notifying handler " + handler, xx);
+ }
+ catch (Error xx)
+ {
+ logger.info("Exception while notifying handler " + handler, xx);
+ throw xx;
}
}
+ public int getWindowSize()
+ {
+ return flowControlStrategy.getWindowSize(this);
+ }
+
+ public void setWindowSize(int initialWindowSize)
+ {
+ flowControlStrategy.setWindowSize(this, initialWindowSize);
+ }
+
+ public String toString()
+ {
+ return String.format("%s@%x{v%d,queuSize=%d,windowSize=%d,streams=%d}", getClass().getSimpleName(), hashCode(), version, queue.size(), getWindowSize(), streams.size());
+ }
+
+
+ @Override
+ public String dump()
+ {
+ return AggregateLifeCycle.dump(this);
+ }
+
+ @Override
+ public void dump(Appendable out, String indent) throws IOException
+ {
+ AggregateLifeCycle.dumpObject(out,this);
+ AggregateLifeCycle.dump(out,indent,Collections.singletonList(controller),streams.values());
+ }
+
+
+
public interface FrameBytes extends Comparable<FrameBytes>
{
public IStream getStream();
@@ -974,14 +1128,14 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
private abstract class AbstractFrameBytes<C> implements FrameBytes, Runnable
{
private final IStream stream;
- private final Callback<C> callback;
+ private final Handler<C> handler;
private final C context;
protected volatile ScheduledFuture<?> task;
- protected AbstractFrameBytes(IStream stream, Callback<C> callback, C context)
+ protected AbstractFrameBytes(IStream stream, Handler<C> handler, C context)
{
this.stream = stream;
- this.callback = callback;
+ this.handler = handler;
this.context = context;
}
@@ -994,22 +1148,31 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
@Override
public int compareTo(FrameBytes that)
{
- // If this.stream.priority > that.stream.priority => -1 (this.stream has less priority than that.stream)
- return that.getStream().getPriority() - getStream().getPriority();
+ // FrameBytes may have or not have a related stream (for example, PING do not have a related stream)
+ // FrameBytes without related streams have higher priority
+ IStream thisStream = getStream();
+ IStream thatStream = that.getStream();
+ if (thisStream == null)
+ return thatStream == null ? 0 : -1;
+ if (thatStream == null)
+ return 1;
+ // If this.stream.priority > that.stream.priority => this.stream has less priority than that.stream
+ return thatStream.getPriority() - thisStream.getPriority();
}
@Override
public void complete()
{
cancelTask();
- StandardSession.this.complete(callback,context);
+ StandardSession.this.complete(handler,context);
}
@Override
public void fail(Throwable x)
{
cancelTask();
- notifyHandlerFailed(callback, context, x);
+ notifyHandlerFailed(handler,context,x);
+ StandardSession.this.flush();
}
private void cancelTask()
@@ -1032,9 +1195,9 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
private final ControlFrame frame;
private final ByteBuffer buffer;
- private ControlFrameBytes(IStream stream, Callback<C> callback, C context, ControlFrame frame, ByteBuffer buffer)
+ private ControlFrameBytes(IStream stream, Handler<C> handler, C context, ControlFrame frame, ByteBuffer buffer)
{
- super(stream, callback,context);
+ super(stream,handler,context);
this.frame = frame;
this.buffer = buffer;
}
@@ -1058,6 +1221,9 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
// Recipients will know the last good stream id and act accordingly.
close();
}
+ IStream stream = getStream();
+ if (stream != null && stream.isClosed())
+ removeStream(stream);
}
@Override
@@ -1073,9 +1239,9 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
private int size;
private volatile ByteBuffer buffer;
- private DataFrameBytes(IStream stream, Callback<C> callback, C context, DataInfo dataInfo)
+ private DataFrameBytes(IStream stream, Handler<C> handler, C context, DataInfo dataInfo)
{
- super(stream, callback,context);
+ super(stream,handler,context);
this.dataInfo = dataInfo;
}
@@ -1108,14 +1274,14 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
{
bufferPool.release(buffer);
IStream stream = getStream();
- stream.updateWindowSize(-size);
-
+ flowControlStrategy.updateWindow(StandardSession.this, stream, -size);
if (dataInfo.available() > 0)
{
// We have written a frame out of this DataInfo, but there is more to write.
// We need to keep the correct ordering of frames, to avoid that another
// DataInfo for the same stream is written before this one is finished.
prepend(this);
+ flush();
}
else
{
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardStream.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardStream.java
index d835c9e..360a639 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardStream.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardStream.java
@@ -1,22 +1,18 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy;
-import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
@@ -25,23 +21,18 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
-import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
import org.eclipse.jetty.spdy.api.DataInfo;
+import org.eclipse.jetty.spdy.api.Handler;
import org.eclipse.jetty.spdy.api.HeadersInfo;
import org.eclipse.jetty.spdy.api.ReplyInfo;
import org.eclipse.jetty.spdy.api.RstInfo;
-import org.eclipse.jetty.spdy.api.Session;
import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.api.StreamFrameListener;
import org.eclipse.jetty.spdy.api.StreamStatus;
import org.eclipse.jetty.spdy.api.SynInfo;
import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.DataFrame;
import org.eclipse.jetty.spdy.frames.HeadersFrame;
import org.eclipse.jetty.spdy.frames.SynReplyFrame;
-import org.eclipse.jetty.spdy.frames.SynStreamFrame;
-import org.eclipse.jetty.spdy.frames.WindowUpdateFrame;
-import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -49,28 +40,29 @@ public class StandardStream implements IStream
{
private static final Logger logger = Log.getLogger(Stream.class);
private final Map<String, Object> attributes = new ConcurrentHashMap<>();
- private final IStream associatedStream;
- private final SynStreamFrame frame;
+ private final int id;
+ private final byte priority;
private final ISession session;
- private final AtomicInteger windowSize;
+ private final IStream associatedStream;
+ private final AtomicInteger windowSize = new AtomicInteger();
private final Set<Stream> pushedStreams = Collections.newSetFromMap(new ConcurrentHashMap<Stream, Boolean>());
private volatile StreamFrameListener listener;
private volatile OpenState openState = OpenState.SYN_SENT;
private volatile CloseState closeState = CloseState.OPENED;
private volatile boolean reset = false;
- public StandardStream(SynStreamFrame frame, ISession session, int windowSize, IStream associatedStream)
+ public StandardStream(int id, byte priority, ISession session, IStream associatedStream)
{
- this.frame = frame;
+ this.id = id;
+ this.priority = priority;
this.session = session;
- this.windowSize = new AtomicInteger(windowSize);
this.associatedStream = associatedStream;
}
@Override
public int getId()
{
- return frame.getStreamId();
+ return id;
}
@Override
@@ -100,7 +92,7 @@ public class StandardStream implements IStream
@Override
public byte getPriority()
{
- return frame.getPriority();
+ return priority;
}
@Override
@@ -113,11 +105,11 @@ public class StandardStream implements IStream
public void updateWindowSize(int delta)
{
int size = windowSize.addAndGet(delta);
- logger.debug("Updated window size by {}, new window size {}",delta,size);
+ logger.debug("Updated window size {} -> {} for {}", size - delta, size, this);
}
@Override
- public Session getSession()
+ public ISession getSession()
{
return session;
}
@@ -146,6 +138,11 @@ public class StandardStream implements IStream
this.listener = listener;
}
+ public StreamFrameListener getStreamFrameListener()
+ {
+ return listener;
+ }
+
@Override
public void updateCloseState(boolean close, boolean local)
{
@@ -155,7 +152,7 @@ public class StandardStream implements IStream
{
case OPENED:
{
- closeState = local?CloseState.LOCALLY_CLOSED:CloseState.REMOTELY_CLOSED;
+ closeState = local ? CloseState.LOCALLY_CLOSED : CloseState.REMOTELY_CLOSED;
break;
}
case LOCALLY_CLOSED:
@@ -196,25 +193,19 @@ public class StandardStream implements IStream
{
openState = OpenState.REPLY_RECV;
SynReplyFrame synReply = (SynReplyFrame)frame;
- updateCloseState(synReply.isClose(),false);
- ReplyInfo replyInfo = new ReplyInfo(synReply.getHeaders(),synReply.isClose());
+ updateCloseState(synReply.isClose(), false);
+ ReplyInfo replyInfo = new ReplyInfo(synReply.getHeaders(), synReply.isClose());
notifyOnReply(replyInfo);
break;
}
case HEADERS:
{
HeadersFrame headers = (HeadersFrame)frame;
- updateCloseState(headers.isClose(),false);
- HeadersInfo headersInfo = new HeadersInfo(headers.getHeaders(),headers.isClose(),headers.isResetCompression());
+ updateCloseState(headers.isClose(), false);
+ HeadersInfo headersInfo = new HeadersInfo(headers.getHeaders(), headers.isClose(), headers.isResetCompression());
notifyOnHeaders(headersInfo);
break;
}
- case WINDOW_UPDATE:
- {
- WindowUpdateFrame windowUpdate = (WindowUpdateFrame)frame;
- updateWindowSize(windowUpdate.getWindowDelta());
- break;
- }
case RST_STREAM:
{
reset = true;
@@ -229,57 +220,28 @@ public class StandardStream implements IStream
}
@Override
- public void process(DataFrame frame, ByteBuffer data)
+ public void process(DataInfo dataInfo)
{
// TODO: in v3 we need to send a rst instead of just ignoring
// ignore data frame if this stream is remotelyClosed already
- if (isHalfClosed() && !isLocallyClosed())
+ if (isRemotelyClosed())
{
- logger.debug("Ignoring received dataFrame as this stream is remotely closed: " + frame);
+ logger.debug("Stream is remotely closed, ignoring {}", dataInfo);
return;
}
if (!canReceive())
{
- logger.debug("Can't receive. Sending rst: " + frame);
- session.rst(new RstInfo(getId(),StreamStatus.PROTOCOL_ERROR));
+ logger.debug("Protocol error receiving {}, resetting" + dataInfo);
+ session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR));
return;
}
- updateCloseState(frame.isClose(),false);
-
- ByteBufferDataInfo dataInfo = new ByteBufferDataInfo(data,frame.isClose(),frame.isCompress())
- {
- @Override
- public void consume(int delta)
- {
- super.consume(delta);
-
- // This is the algorithm for flow control.
- // This method may be called multiple times with delta=1, but we only send a window
- // update when the whole dataInfo has been consumed.
- // Other policies may be to send window updates when consumed() is greater than
- // a certain threshold, etc. but for now the policy is not pluggable for simplicity.
- // Note that the frequency of window updates depends on the read buffer, that
- // should not be too smaller than the window size to avoid frequent window updates.
- // Therefore, a pluggable policy should be able to modify the read buffer capacity.
- if (consumed() == length() && !isClosed())
- windowUpdate(length());
- }
- };
+ updateCloseState(dataInfo.isClose(), false);
notifyOnData(dataInfo);
session.flush();
}
- private void windowUpdate(int delta)
- {
- if (delta > 0)
- {
- WindowUpdateFrame windowUpdateFrame = new WindowUpdateFrame(session.getVersion(),getId(),delta);
- session.control(this,windowUpdateFrame,0,TimeUnit.MILLISECONDS,null,null);
- }
- }
-
private void notifyOnReply(ReplyInfo replyInfo)
{
final StreamFrameListener listener = this.listener;
@@ -287,13 +249,18 @@ public class StandardStream implements IStream
{
if (listener != null)
{
- logger.debug("Invoking reply callback with {} on listener {}",replyInfo,listener);
- listener.onReply(this,replyInfo);
+ logger.debug("Invoking reply callback with {} on listener {}", replyInfo, listener);
+ listener.onReply(this, replyInfo);
}
}
catch (Exception x)
{
- logger.info("Exception while notifying listener " + listener,x);
+ logger.info("Exception while notifying listener " + listener, x);
+ }
+ catch (Error x)
+ {
+ logger.info("Exception while notifying listener " + listener, x);
+ throw x;
}
}
@@ -304,13 +271,18 @@ public class StandardStream implements IStream
{
if (listener != null)
{
- logger.debug("Invoking headers callback with {} on listener {}",frame,listener);
- listener.onHeaders(this,headersInfo);
+ logger.debug("Invoking headers callback with {} on listener {}", headersInfo, listener);
+ listener.onHeaders(this, headersInfo);
}
}
catch (Exception x)
{
- logger.info("Exception while notifying listener " + listener,x);
+ logger.info("Exception while notifying listener " + listener, x);
+ }
+ catch (Error x)
+ {
+ logger.info("Exception while notifying listener " + listener, x);
+ throw x;
}
}
@@ -321,14 +293,19 @@ public class StandardStream implements IStream
{
if (listener != null)
{
- logger.debug("Invoking data callback with {} on listener {}",dataInfo,listener);
- listener.onData(this,dataInfo);
- logger.debug("Invoked data callback with {} on listener {}",dataInfo,listener);
+ logger.debug("Invoking data callback with {} on listener {}", dataInfo, listener);
+ listener.onData(this, dataInfo);
+ logger.debug("Invoked data callback with {} on listener {}", dataInfo, listener);
}
}
catch (Exception x)
{
- logger.info("Exception while notifying listener " + listener,x);
+ logger.info("Exception while notifying listener " + listener, x);
+ }
+ catch (Error x)
+ {
+ logger.info("Exception while notifying listener " + listener, x);
+ throw x;
}
}
@@ -341,15 +318,15 @@ public class StandardStream implements IStream
}
@Override
- public void syn(SynInfo synInfo, long timeout, TimeUnit unit, Callback<Stream> callback)
+ public void syn(SynInfo synInfo, long timeout, TimeUnit unit, Handler<Stream> handler)
{
if (isClosed() || isReset())
{
- callback.failed(this, new StreamException(getId(), StreamStatus.STREAM_ALREADY_CLOSED));
+ handler.failed(this, new StreamException(getId(), StreamStatus.STREAM_ALREADY_CLOSED));
return;
}
PushSynInfo pushSynInfo = new PushSynInfo(getId(), synInfo);
- session.syn(pushSynInfo, null, timeout, unit, callback);
+ session.syn(pushSynInfo, null, timeout, unit, handler);
}
@Override
@@ -361,14 +338,14 @@ public class StandardStream implements IStream
}
@Override
- public void reply(ReplyInfo replyInfo, long timeout, TimeUnit unit, Callback<Void> callback)
+ public void reply(ReplyInfo replyInfo, long timeout, TimeUnit unit, Handler<Void> handler)
{
if (isUnidirectional())
throw new IllegalStateException("Protocol violation: cannot send SYN_REPLY frames in unidirectional streams");
openState = OpenState.REPLY_SENT;
- updateCloseState(replyInfo.isClose(),true);
- SynReplyFrame frame = new SynReplyFrame(session.getVersion(),replyInfo.getFlags(),getId(),replyInfo.getHeaders());
- session.control(this,frame,timeout,unit, callback,null);
+ updateCloseState(replyInfo.isClose(), true);
+ SynReplyFrame frame = new SynReplyFrame(session.getVersion(), replyInfo.getFlags(), getId(), replyInfo.getHeaders());
+ session.control(this, frame, timeout, unit, handler, null);
}
@Override
@@ -380,22 +357,22 @@ public class StandardStream implements IStream
}
@Override
- public void data(DataInfo dataInfo, long timeout, TimeUnit unit, Callback<Void> callback)
+ public void data(DataInfo dataInfo, long timeout, TimeUnit unit, Handler<Void> handler)
{
if (!canSend())
{
- session.rst(new RstInfo(getId(),StreamStatus.PROTOCOL_ERROR));
+ session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR));
throw new IllegalStateException("Protocol violation: cannot send a DATA frame before a SYN_REPLY frame");
}
if (isLocallyClosed())
{
- session.rst(new RstInfo(getId(),StreamStatus.PROTOCOL_ERROR));
+ session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR));
throw new IllegalStateException("Protocol violation: cannot send a DATA frame on a closed stream");
}
// Cannot update the close state here, because the data that we send may
// be flow controlled, so we need the stream to update the window size.
- session.data(this,dataInfo,timeout,unit, callback,null);
+ session.data(this, dataInfo, timeout, unit, handler, null);
}
@Override
@@ -407,22 +384,22 @@ public class StandardStream implements IStream
}
@Override
- public void headers(HeadersInfo headersInfo, long timeout, TimeUnit unit, Callback<Void> callback)
+ public void headers(HeadersInfo headersInfo, long timeout, TimeUnit unit, Handler<Void> handler)
{
if (!canSend())
{
- session.rst(new RstInfo(getId(),StreamStatus.PROTOCOL_ERROR));
+ session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR));
throw new IllegalStateException("Protocol violation: cannot send a HEADERS frame before a SYN_REPLY frame");
}
if (isLocallyClosed())
{
- session.rst(new RstInfo(getId(),StreamStatus.PROTOCOL_ERROR));
+ session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR));
throw new IllegalStateException("Protocol violation: cannot send a HEADERS frame on a closed stream");
}
- updateCloseState(headersInfo.isClose(),true);
- HeadersFrame frame = new HeadersFrame(session.getVersion(),headersInfo.getFlags(),getId(),headersInfo.getHeaders());
- session.control(this,frame,timeout,unit, callback,null);
+ updateCloseState(headersInfo.isClose(), true);
+ HeadersFrame frame = new HeadersFrame(session.getVersion(), headersInfo.getFlags(), getId(), headersInfo.getHeaders());
+ session.control(this, frame, timeout, unit, handler, null);
}
@Override
@@ -456,10 +433,16 @@ public class StandardStream implements IStream
return closeState == CloseState.LOCALLY_CLOSED || closeState == CloseState.CLOSED;
}
+ private boolean isRemotelyClosed()
+ {
+ CloseState closeState = this.closeState;
+ return closeState == CloseState.REMOTELY_CLOSED || closeState == CloseState.CLOSED;
+ }
+
@Override
public String toString()
{
- return String.format("stream=%d v%d %s",getId(),session.getVersion(),closeState);
+ return String.format("stream=%d v%d windowSize=%db reset=%s %s %s", getId(), session.getVersion(), getWindowSize(), isReset(), openState, closeState);
}
private boolean canSend()
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StreamException.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StreamException.java
index 5b4bd7f..4a0a954 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StreamException.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StreamException.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/ByteBufferDataInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/ByteBufferDataInfo.java
index 678ff51..3b856f6 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/ByteBufferDataInfo.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/ByteBufferDataInfo.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
@@ -68,4 +65,10 @@ public class ByteBufferDataInfo extends DataInfo
}
return space;
}
+
+ @Override
+ protected ByteBuffer allocate(int size)
+ {
+ return buffer.isDirect() ? ByteBuffer.allocateDirect(size) : super.allocate(size);
+ }
}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/BytesDataInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/BytesDataInfo.java
index f215084..9d2123b 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/BytesDataInfo.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/BytesDataInfo.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
@@ -23,39 +20,44 @@ import java.nio.ByteBuffer;
*/
public class BytesDataInfo extends DataInfo
{
- private byte[] bytes;
- private int offset;
+ private final byte[] bytes;
+ private final int offset;
+ private final int length;
+ private int index;
public BytesDataInfo(byte[] bytes, boolean close)
{
- this(bytes, close, false);
+ this(bytes, 0, bytes.length, close);
}
- public BytesDataInfo(byte[] bytes, boolean close, boolean compress)
+ public BytesDataInfo(byte[] bytes, int offset, int length, boolean close)
{
- super(close, compress);
+ super(close, false);
this.bytes = bytes;
+ this.offset = offset;
+ this.length = length;
+ this.index = offset;
}
@Override
public int length()
{
- return bytes.length;
+ return length;
}
@Override
public int available()
{
- return length() - offset;
+ return length - index + offset;
}
@Override
public int readInto(ByteBuffer output)
{
int space = output.remaining();
- int length = Math.min(available(), space);
- output.put(bytes, offset, length);
- offset += length;
- return length;
+ int chunk = Math.min(available(), space);
+ output.put(bytes, index, chunk);
+ index += chunk;
+ return chunk;
}
}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/DataInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/DataInfo.java
index 4036401..8253e40 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/DataInfo.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/DataInfo.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/GoAwayInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/GoAwayInfo.java
index 4ebc726..1e16e34 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/GoAwayInfo.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/GoAwayInfo.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Handler.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Handler.java
new file mode 100644
index 0000000..b69467e
--- /dev/null
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Handler.java
@@ -0,0 +1,56 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
+
+/**
+ * <p>A callback abstraction that handles completed/failed events of asynchronous operations.</p>
+ * <p>Instances of this class capture a context that is made available on the completion callback.</p>
+ *
+ * @param <C> the type of the context object
+ */
+public interface Handler<C>
+{
+ /**
+ * <p>Callback invoked when the operation completes.</p>
+ *
+ * @param context the context
+ * @see #failed(Object, Throwable)
+ */
+ public abstract void completed(C context);
+
+ /**
+ * <p>Callback invoked when the operation fails.</p>
+ * @param context the context
+ * @param x the reason for the operation failure
+ */
+ public void failed(C context, Throwable x);
+
+ /**
+ * <p>Empty implementation of {@link Handler}</p>
+ *
+ * @param <C> the type of the context object
+ */
+ public static class Adapter<C> implements Handler<C>
+ {
+ @Override
+ public void completed(C context)
+ {
+ }
+
+ @Override
+ public void failed(C context, Throwable x)
+ {
+ }
+ }
+}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Headers.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Headers.java
index 3161500..261a94f 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Headers.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Headers.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
@@ -219,13 +216,15 @@ public class Headers implements Iterable<Headers.Header>
if (obj == null || getClass() != obj.getClass())
return false;
Header that = (Header)obj;
- return name.equals(that.name) && Arrays.equals(values, that.values);
+ // Header names must be lowercase, thus we lowercase them before transmission, but keep them as is
+ // internally. That's why we've to compare them case insensitive.
+ return name.equalsIgnoreCase(that.name) && Arrays.equals(values, that.values);
}
@Override
public int hashCode()
{
- int result = name.hashCode();
+ int result = name.toLowerCase().hashCode();
result = 31 * result + Arrays.hashCode(values);
return result;
}
@@ -269,6 +268,21 @@ public class Headers implements Iterable<Headers.Header>
}
/**
+ * @return the values as a comma separated list
+ */
+ public String valuesAsString()
+ {
+ StringBuilder result = new StringBuilder();
+ for (int i = 0; i < values.length; ++i)
+ {
+ if (i > 0)
+ result.append(", ");
+ result.append(values[i]);
+ }
+ return result.toString();
+ }
+
+ /**
* @return whether the header has multiple values
*/
public boolean hasMultipleValues()
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/HeadersInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/HeadersInfo.java
index 62ae07f..97a6a7e 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/HeadersInfo.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/HeadersInfo.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/PingInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/PingInfo.java
index bbcd9a8..28bc7a7 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/PingInfo.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/PingInfo.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/ReplyInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/ReplyInfo.java
index 9ce0ef6..9e51143 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/ReplyInfo.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/ReplyInfo.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/RstInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/RstInfo.java
index f1f5ee4..101fd71 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/RstInfo.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/RstInfo.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SPDY.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SPDY.java
index 8ec2d8d..31d7620 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SPDY.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SPDY.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SPDYException.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SPDYException.java
index a0585f7..af21428 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SPDYException.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SPDYException.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Session.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Session.java
index b16c553..bbe17fc 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Session.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Session.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
@@ -21,8 +18,6 @@ import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
-import org.eclipse.jetty.util.Callback;
-
/**
* <p>A {@link Session} represents the client-side endpoint of a SPDY connection to a single origin server.</p>
* <p>Once a {@link Session} has been obtained, it can be used to open SPDY streams:</p>
@@ -74,7 +69,7 @@ public interface Session
* @param synInfo the metadata to send on stream creation
* @param listener the listener to invoke when events happen on the stream just created
* @return a future for the stream that will be created
- * @see #syn(SynInfo, StreamFrameListener, long, TimeUnit, Callback)
+ * @see #syn(SynInfo, StreamFrameListener, long, TimeUnit, Handler)
*/
public Future<Stream> syn(SynInfo synInfo, StreamFrameListener listener);
@@ -87,10 +82,10 @@ public interface Session
* @param listener the listener to invoke when events happen on the stream just created
* @param timeout the operation's timeout
* @param unit the timeout's unit
- * @param callback the completion handler that gets notified of stream creation
+ * @param handler the completion handler that gets notified of stream creation
* @see #syn(SynInfo, StreamFrameListener)
*/
- public void syn(SynInfo synInfo, StreamFrameListener listener, long timeout, TimeUnit unit, Callback<Stream> callback);
+ public void syn(SynInfo synInfo, StreamFrameListener listener, long timeout, TimeUnit unit, Handler<Stream> handler);
/**
@@ -99,7 +94,7 @@ public interface Session
*
* @param rstInfo the metadata to reset the stream
* @return a future to wait for the reset to be sent
- * @see #rst(RstInfo, long, TimeUnit, Callback)
+ * @see #rst(RstInfo, long, TimeUnit, Handler)
*/
public Future<Void> rst(RstInfo rstInfo);
@@ -111,10 +106,10 @@ public interface Session
* @param rstInfo the metadata to reset the stream
* @param timeout the operation's timeout
* @param unit the timeout's unit
- * @param callback the completion handler that gets notified of reset's send
+ * @param handler the completion handler that gets notified of reset's send
* @see #rst(RstInfo)
*/
- public void rst(RstInfo rstInfo, long timeout, TimeUnit unit, Callback<Void> callback);
+ public void rst(RstInfo rstInfo, long timeout, TimeUnit unit, Handler<Void> handler);
/**
* <p>Sends asynchronously a SETTINGS to configure the SPDY connection.</p>
@@ -122,7 +117,7 @@ public interface Session
*
* @param settingsInfo the metadata to send
* @return a future to wait for the settings to be sent
- * @see #settings(SettingsInfo, long, TimeUnit, Callback)
+ * @see #settings(SettingsInfo, long, TimeUnit, Handler)
*/
public Future<Void> settings(SettingsInfo settingsInfo);
@@ -134,17 +129,17 @@ public interface Session
* @param settingsInfo the metadata to send
* @param timeout the operation's timeout
* @param unit the timeout's unit
- * @param callback the completion handler that gets notified of settings' send
+ * @param handler the completion handler that gets notified of settings' send
* @see #settings(SettingsInfo)
*/
- public void settings(SettingsInfo settingsInfo, long timeout, TimeUnit unit, Callback<Void> callback);
+ public void settings(SettingsInfo settingsInfo, long timeout, TimeUnit unit, Handler<Void> handler);
/**
* <p>Sends asynchronously a PING, normally to measure round-trip time.</p>
* <p>Callers may use the returned future to wait for the ping to be sent.</p>
*
* @return a future for the metadata sent
- * @see #ping(long, TimeUnit, Callback)
+ * @see #ping(long, TimeUnit, Handler)
*/
public Future<PingInfo> ping();
@@ -155,17 +150,17 @@ public interface Session
*
* @param timeout the operation's timeout
* @param unit the timeout's unit
- * @param callback the completion handler that gets notified of ping's send
+ * @param handler the completion handler that gets notified of ping's send
* @see #ping()
*/
- public void ping(long timeout, TimeUnit unit, Callback<PingInfo> callback);
+ public void ping(long timeout, TimeUnit unit, Handler<PingInfo> handler);
/**
* <p>Closes gracefully this session, sending a GO_AWAY frame and then closing the TCP connection.</p>
* <p>Callers may use the returned future to wait for the go away to be sent.</p>
*
* @return a future to wait for the go away to be sent
- * @see #goAway(long, TimeUnit, Callback)
+ * @see #goAway(long, TimeUnit, Handler)
*/
public Future<Void> goAway();
@@ -176,17 +171,47 @@ public interface Session
*
* @param timeout the operation's timeout
* @param unit the timeout's unit
- * @param callback the completion handler that gets notified of go away's send
+ * @param handler the completion handler that gets notified of go away's send
* @see #goAway()
*/
- public void goAway(long timeout, TimeUnit unit, Callback<Void> callback);
+ public void goAway(long timeout, TimeUnit unit, Handler<Void> handler);
/**
- * @return the streams currently active in this session
+ * @return a snapshot of the streams currently active in this session
+ * @see #getStream(int)
*/
public Set<Stream> getStreams();
/**
+ * @param streamId the id of the stream to retrieve
+ * @return the stream with the given stream id
+ * @see #getStreams()
+ */
+ public Stream getStream(int streamId);
+
+ /**
+ * @param key the attribute key
+ * @return an arbitrary object associated with the given key to this session
+ * @see #setAttribute(String, Object)
+ */
+ public Object getAttribute(String key);
+
+ /**
+ * @param key the attribute key
+ * @param value an arbitrary object to associate with the given key to this session
+ * @see #getAttribute(String)
+ * @see #removeAttribute(String)
+ */
+ public void setAttribute(String key, Object value);
+
+ /**
+ * @param key the attribute key
+ * @return the arbitrary object associated with the given key to this session
+ * @see #setAttribute(String, Object)
+ */
+ public Object removeAttribute(String key);
+
+ /**
* <p>Super interface for listeners with callbacks that are invoked on specific session events.</p>
*/
public interface Listener extends EventListener
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionFrameListener.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionFrameListener.java
index 467919c..7e507e3 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionFrameListener.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionFrameListener.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionStatus.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionStatus.java
index c9c40d1..56e9355 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionStatus.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionStatus.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Settings.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Settings.java
index 6db03e8..6ed59a0 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Settings.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Settings.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SettingsInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SettingsInfo.java
index 3823382..d622189 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SettingsInfo.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SettingsInfo.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Stream.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Stream.java
index 84d7ab2..092c7a3 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Stream.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Stream.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
@@ -21,8 +18,6 @@ import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
-import org.eclipse.jetty.util.Callback;
-
/**
* <p>A {@link Stream} represents a bidirectional exchange of data on top of a {@link Session}.</p>
* <p>Differently from socket streams, where the input and output streams are permanently associated
@@ -47,7 +42,7 @@ import org.eclipse.jetty.util.Callback;
* stream.data(StringDataInfo("chunk1", false), 5, TimeUnit.SECONDS, new Handler&lt;Void&gt;() { ... });
* stream.data(StringDataInfo("chunk2", true), 1, TimeUnit.SECONDS, new Handler&lt;Void&gt;() { ... });
* </pre>
- * <p>where the second call to {@link #data(DataInfo, long, TimeUnit, Callback)} has a timeout smaller
+ * <p>where the second call to {@link #data(DataInfo, long, TimeUnit, Handler)} has a timeout smaller
* than the previous call.</p>
* <p>The behavior of such style of invocations is unspecified (it may even throw an exception - similar
* to {@link WritePendingException}).</p>
@@ -94,7 +89,7 @@ public interface Stream
*
* @param synInfo the metadata to send on stream creation
* @return a future containing the stream once it got established
- * @see #syn(SynInfo, long, TimeUnit, Callback)
+ * @see #syn(SynInfo, long, TimeUnit, Handler)
*/
public Future<Stream> syn(SynInfo synInfo);
@@ -106,10 +101,10 @@ public interface Stream
* @param synInfo the metadata to send on stream creation
* @param timeout the operation's timeout
* @param unit the timeout's unit
- * @param callback the completion handler that gets notified once the pushstream is established
+ * @param handler the completion handler that gets notified once the pushstream is established
* @see #syn(SynInfo)
*/
- public void syn(SynInfo synInfo, long timeout, TimeUnit unit, Callback<Stream> callback);
+ public void syn(SynInfo synInfo, long timeout, TimeUnit unit, Handler<Stream> handler);
/**
* <p>Sends asynchronously a SYN_REPLY frame in response to a SYN_STREAM frame.</p>
@@ -117,7 +112,7 @@ public interface Stream
*
* @param replyInfo the metadata to send
* @return a future to wait for the reply to be sent
- * @see #reply(ReplyInfo, long, TimeUnit, Callback)
+ * @see #reply(ReplyInfo, long, TimeUnit, Handler)
* @see SessionFrameListener#onSyn(Stream, SynInfo)
*/
public Future<Void> reply(ReplyInfo replyInfo);
@@ -130,10 +125,10 @@ public interface Stream
* @param replyInfo the metadata to send
* @param timeout the operation's timeout
* @param unit the timeout's unit
- * @param callback the completion handler that gets notified of reply sent
+ * @param handler the completion handler that gets notified of reply sent
* @see #reply(ReplyInfo)
*/
- public void reply(ReplyInfo replyInfo, long timeout, TimeUnit unit, Callback<Void> callback);
+ public void reply(ReplyInfo replyInfo, long timeout, TimeUnit unit, Handler<Void> handler);
/**
* <p>Sends asynchronously a DATA frame on this stream.</p>
@@ -142,7 +137,7 @@ public interface Stream
*
* @param dataInfo the metadata to send
* @return a future to wait for the data to be sent
- * @see #data(DataInfo, long, TimeUnit, Callback)
+ * @see #data(DataInfo, long, TimeUnit, Handler)
* @see #reply(ReplyInfo)
*/
public Future<Void> data(DataInfo dataInfo);
@@ -156,10 +151,10 @@ public interface Stream
* @param dataInfo the metadata to send
* @param timeout the operation's timeout
* @param unit the timeout's unit
- * @param callback the completion handler that gets notified of data sent
+ * @param handler the completion handler that gets notified of data sent
* @see #data(DataInfo)
*/
- public void data(DataInfo dataInfo, long timeout, TimeUnit unit, Callback<Void> callback);
+ public void data(DataInfo dataInfo, long timeout, TimeUnit unit, Handler<Void> handler);
/**
* <p>Sends asynchronously a HEADER frame on this stream.</p>
@@ -168,7 +163,7 @@ public interface Stream
*
* @param headersInfo the metadata to send
* @return a future to wait for the headers to be sent
- * @see #headers(HeadersInfo, long, TimeUnit, Callback)
+ * @see #headers(HeadersInfo, long, TimeUnit, Handler)
* @see #reply(ReplyInfo)
*/
public Future<Void> headers(HeadersInfo headersInfo);
@@ -182,10 +177,10 @@ public interface Stream
* @param headersInfo the metadata to send
* @param timeout the operation's timeout
* @param unit the timeout's unit
- * @param callback the completion handler that gets notified of headers sent
+ * @param handler the completion handler that gets notified of headers sent
* @see #headers(HeadersInfo)
*/
- public void headers(HeadersInfo headersInfo, long timeout, TimeUnit unit, Callback<Void> callback);
+ public void headers(HeadersInfo headersInfo, long timeout, TimeUnit unit, Handler<Void> handler);
/**
* @return whether this stream is unidirectional or not
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StreamFrameListener.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StreamFrameListener.java
index a279f38..23b5b16 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StreamFrameListener.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StreamFrameListener.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StreamStatus.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StreamStatus.java
index c9908c8..b3e48e0 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StreamStatus.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StreamStatus.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StringDataInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StringDataInfo.java
index 41e9e62..164c6fb 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StringDataInfo.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StringDataInfo.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
@@ -25,11 +22,6 @@ public class StringDataInfo extends BytesDataInfo
{
public StringDataInfo(String string, boolean close)
{
- this(string, close, false);
- }
-
- public StringDataInfo(String string, boolean close, boolean compress)
- {
- super(string.getBytes(Charset.forName("UTF-8")), close, compress);
+ super(string.getBytes(Charset.forName("UTF-8")), close);
}
}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SynInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SynInfo.java
index c51a001..3f8eb60 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SynInfo.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SynInfo.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/server/ServerSessionFrameListener.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/server/ServerSessionFrameListener.java
index 7941550..7b330a9 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/server/ServerSessionFrameListener.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/server/ServerSessionFrameListener.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api.server;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/ControlFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/ControlFrame.java
index afd554c..a9c7b0a 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/ControlFrame.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/ControlFrame.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/ControlFrameType.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/ControlFrameType.java
index bf638d3..1fb2834 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/ControlFrameType.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/ControlFrameType.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
@@ -29,7 +26,8 @@ public enum ControlFrameType
PING((short)6),
GO_AWAY((short)7),
HEADERS((short)8),
- WINDOW_UPDATE((short)9);
+ WINDOW_UPDATE((short)9),
+ CREDENTIAL((short)10);
public static ControlFrameType from(short code)
{
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/CredentialFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/CredentialFrame.java
new file mode 100644
index 0000000..5bd882f
--- /dev/null
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/CredentialFrame.java
@@ -0,0 +1,46 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
+
+import java.security.cert.Certificate;
+
+public class CredentialFrame extends ControlFrame
+{
+ private final short slot;
+ private final byte[] proof;
+ private final Certificate[] certificateChain;
+
+ public CredentialFrame(short version, short slot, byte[] proof, Certificate[] certificateChain)
+ {
+ super(version, ControlFrameType.CREDENTIAL, (byte)0);
+ this.slot = slot;
+ this.proof = proof;
+ this.certificateChain = certificateChain;
+ }
+
+ public short getSlot()
+ {
+ return slot;
+ }
+
+ public byte[] getProof()
+ {
+ return proof;
+ }
+
+ public Certificate[] getCertificateChain()
+ {
+ return certificateChain;
+ }
+}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/DataFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/DataFrame.java
index 3ce17ee..e261bf8 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/DataFrame.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/DataFrame.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/GoAwayFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/GoAwayFrame.java
index bda5755..15f04f1 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/GoAwayFrame.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/GoAwayFrame.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/HeadersFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/HeadersFrame.java
index e0545b6..ea88513 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/HeadersFrame.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/HeadersFrame.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/NoOpFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/NoOpFrame.java
index bf9eac0..e4cffcc 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/NoOpFrame.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/NoOpFrame.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/PingFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/PingFrame.java
index aa1ac54..3989a09 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/PingFrame.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/PingFrame.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/RstStreamFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/RstStreamFrame.java
index 334b816..0877da4 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/RstStreamFrame.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/RstStreamFrame.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SettingsFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SettingsFrame.java
index afddb37..8b676cd 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SettingsFrame.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SettingsFrame.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SynReplyFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SynReplyFrame.java
index 9108168..82888b1 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SynReplyFrame.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SynReplyFrame.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SynStreamFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SynStreamFrame.java
index 1b40895..c5aa8d3 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SynStreamFrame.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SynStreamFrame.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
@@ -25,14 +22,16 @@ public class SynStreamFrame extends ControlFrame
private final int streamId;
private final int associatedStreamId;
private final byte priority;
+ private final short slot;
private final Headers headers;
- public SynStreamFrame(short version, byte flags, int streamId, int associatedStreamId, byte priority, Headers headers)
+ public SynStreamFrame(short version, byte flags, int streamId, int associatedStreamId, byte priority, short slot, Headers headers)
{
super(version, ControlFrameType.SYN_STREAM, flags);
this.streamId = streamId;
this.associatedStreamId = associatedStreamId;
this.priority = priority;
+ this.slot = slot;
this.headers = headers;
}
@@ -51,6 +50,11 @@ public class SynStreamFrame extends ControlFrame
return priority;
}
+ public short getSlot()
+ {
+ return slot;
+ }
+
public Headers getHeaders()
{
return headers;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/WindowUpdateFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/WindowUpdateFrame.java
index 22a4129..8ed37d4 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/WindowUpdateFrame.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/WindowUpdateFrame.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/ControlFrameGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/ControlFrameGenerator.java
index 51d98c7..ff7302c 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/ControlFrameGenerator.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/ControlFrameGenerator.java
@@ -1,24 +1,21 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.generator;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.spdy.ByteBufferPool;
import org.eclipse.jetty.spdy.frames.ControlFrame;
public abstract class ControlFrameGenerator
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/CredentialGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/CredentialGenerator.java
new file mode 100644
index 0000000..f2064fb
--- /dev/null
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/CredentialGenerator.java
@@ -0,0 +1,80 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.generator;
+
+import java.nio.ByteBuffer;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.spdy.ByteBufferPool;
+import org.eclipse.jetty.spdy.SessionException;
+import org.eclipse.jetty.spdy.api.SessionStatus;
+import org.eclipse.jetty.spdy.frames.ControlFrame;
+import org.eclipse.jetty.spdy.frames.CredentialFrame;
+
+public class CredentialGenerator extends ControlFrameGenerator
+{
+ public CredentialGenerator(ByteBufferPool bufferPool)
+ {
+ super(bufferPool);
+ }
+
+ @Override
+ public ByteBuffer generate(ControlFrame frame)
+ {
+ CredentialFrame credential = (CredentialFrame)frame;
+
+ byte[] proof = credential.getProof();
+
+ List<byte[]> certificates = serializeCertificates(credential.getCertificateChain());
+ int certificatesLength = 0;
+ for (byte[] certificate : certificates)
+ certificatesLength += certificate.length;
+
+ int frameBodyLength = 2 + 4 + proof.length + certificates.size() * 4 + certificatesLength;
+
+ int totalLength = ControlFrame.HEADER_LENGTH + frameBodyLength;
+ ByteBuffer buffer = getByteBufferPool().acquire(totalLength, true);
+ generateControlFrameHeader(credential, frameBodyLength, buffer);
+
+ buffer.putShort(credential.getSlot());
+ buffer.putInt(proof.length);
+ buffer.put(proof);
+ for (byte[] certificate : certificates)
+ {
+ buffer.putInt(certificate.length);
+ buffer.put(certificate);
+ }
+
+ buffer.flip();
+ return buffer;
+ }
+
+ private List<byte[]> serializeCertificates(Certificate[] certificates)
+ {
+ try
+ {
+ List<byte[]> result = new ArrayList<>(certificates.length);
+ for (Certificate certificate : certificates)
+ result.add(certificate.getEncoded());
+ return result;
+ }
+ catch (CertificateEncodingException x)
+ {
+ throw new SessionException(SessionStatus.PROTOCOL_ERROR, x);
+ }
+ }
+}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/DataFrameGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/DataFrameGenerator.java
index 6a1acfe..aed8fbc 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/DataFrameGenerator.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/DataFrameGenerator.java
@@ -1,24 +1,20 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
+//========================================================================
+//Copyright 2011-2012 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.spdy.generator;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.spdy.ByteBufferPool;
import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.frames.DataFrame;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/Generator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/Generator.java
index 91cd127..cccbf15 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/Generator.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/Generator.java
@@ -1,25 +1,22 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.generator;
import java.nio.ByteBuffer;
import java.util.EnumMap;
-import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.spdy.ByteBufferPool;
import org.eclipse.jetty.spdy.CompressionFactory;
import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.frames.ControlFrame;
@@ -42,6 +39,7 @@ public class Generator
generators.put(ControlFrameType.GO_AWAY, new GoAwayGenerator(bufferPool));
generators.put(ControlFrameType.HEADERS, new HeadersGenerator(bufferPool, headersBlockGenerator));
generators.put(ControlFrameType.WINDOW_UPDATE, new WindowUpdateGenerator(bufferPool));
+ generators.put(ControlFrameType.CREDENTIAL, new CredentialGenerator(bufferPool));
dataFrameGenerator = new DataFrameGenerator(bufferPool);
}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/GoAwayGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/GoAwayGenerator.java
index f22b6b3..dced42a 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/GoAwayGenerator.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/GoAwayGenerator.java
@@ -1,24 +1,21 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.generator;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.spdy.ByteBufferPool;
import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.frames.ControlFrame;
import org.eclipse.jetty.spdy.frames.GoAwayFrame;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersBlockGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersBlockGenerator.java
index 93a2dd6..6290841 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersBlockGenerator.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersBlockGenerator.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.generator;
@@ -43,7 +40,7 @@ public class HeadersBlockGenerator
writeCount(version, buffer, headers.size());
for (Headers.Header header : headers)
{
- String name = header.name();
+ String name = header.name().toLowerCase();
byte[] nameBytes = name.getBytes(iso1);
writeNameLength(version, buffer, nameBytes.length);
buffer.write(nameBytes, 0, nameBytes.length);
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersGenerator.java
index dfe43b4..6f520d9 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersGenerator.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersGenerator.java
@@ -1,25 +1,23 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.generator;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.spdy.ByteBufferPool;
import org.eclipse.jetty.spdy.SessionException;
+import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.api.SessionStatus;
import org.eclipse.jetty.spdy.frames.ControlFrame;
import org.eclipse.jetty.spdy.frames.HeadersFrame;
@@ -43,6 +41,8 @@ public class HeadersGenerator extends ControlFrameGenerator
ByteBuffer headersBuffer = headersBlockGenerator.generate(version, headers.getHeaders());
int frameBodyLength = 4;
+ if (frame.getVersion() == SPDY.V2)
+ frameBodyLength += 2;
int frameLength = frameBodyLength + headersBuffer.remaining();
if (frameLength > 0xFF_FF_FF)
@@ -58,6 +58,8 @@ public class HeadersGenerator extends ControlFrameGenerator
generateControlFrameHeader(headers, frameLength, buffer);
buffer.putInt(headers.getStreamId() & 0x7F_FF_FF_FF);
+ if (frame.getVersion() == SPDY.V2)
+ buffer.putShort((short)0);
buffer.put(headersBuffer);
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/NoOpGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/NoOpGenerator.java
index d543e9c..86a2feb 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/NoOpGenerator.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/NoOpGenerator.java
@@ -1,24 +1,21 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.generator;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.spdy.ByteBufferPool;
import org.eclipse.jetty.spdy.frames.ControlFrame;
import org.eclipse.jetty.spdy.frames.NoOpFrame;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/PingGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/PingGenerator.java
index 3a431ee..0ead892 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/PingGenerator.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/PingGenerator.java
@@ -1,24 +1,21 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.generator;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.spdy.ByteBufferPool;
import org.eclipse.jetty.spdy.frames.ControlFrame;
import org.eclipse.jetty.spdy.frames.PingFrame;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/RstStreamGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/RstStreamGenerator.java
index 5805bf9..63a0142 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/RstStreamGenerator.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/RstStreamGenerator.java
@@ -1,24 +1,21 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.generator;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.spdy.ByteBufferPool;
import org.eclipse.jetty.spdy.frames.ControlFrame;
import org.eclipse.jetty.spdy.frames.RstStreamFrame;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SettingsGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SettingsGenerator.java
index 9f53d45..6004afa 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SettingsGenerator.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SettingsGenerator.java
@@ -1,24 +1,21 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.generator;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.spdy.ByteBufferPool;
import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.api.Settings;
import org.eclipse.jetty.spdy.frames.ControlFrame;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SynReplyGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SynReplyGenerator.java
index a8e656e..fc8f382 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SynReplyGenerator.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SynReplyGenerator.java
@@ -1,24 +1,21 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.generator;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.spdy.ByteBufferPool;
import org.eclipse.jetty.spdy.SessionException;
import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.api.SessionStatus;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SynStreamGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SynStreamGenerator.java
index 4ba21ff..ff6ace8 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SynStreamGenerator.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SynStreamGenerator.java
@@ -1,24 +1,21 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.generator;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.spdy.ByteBufferPool;
import org.eclipse.jetty.spdy.SessionException;
import org.eclipse.jetty.spdy.StreamException;
import org.eclipse.jetty.spdy.api.SPDY;
@@ -64,6 +61,7 @@ public class SynStreamGenerator extends ControlFrameGenerator
buffer.putInt(streamId & 0x7F_FF_FF_FF);
buffer.putInt(synStream.getAssociatedStreamId() & 0x7F_FF_FF_FF);
writePriority(streamId, version, synStream.getPriority(), buffer);
+ buffer.put((byte)synStream.getSlot());
buffer.put(headersBuffer);
@@ -85,6 +83,5 @@ public class SynStreamGenerator extends ControlFrameGenerator
throw new StreamException(streamId, StreamStatus.UNSUPPORTED_VERSION);
}
buffer.put(priority);
- buffer.put((byte)0);
}
}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/WindowUpdateGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/WindowUpdateGenerator.java
index a53b98e..3809280 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/WindowUpdateGenerator.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/WindowUpdateGenerator.java
@@ -1,24 +1,21 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.generator;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.spdy.ByteBufferPool;
import org.eclipse.jetty.spdy.frames.ControlFrame;
import org.eclipse.jetty.spdy.frames.WindowUpdateFrame;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/ControlFrameBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/ControlFrameBodyParser.java
index a9499a8..f820b59 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/ControlFrameBodyParser.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/ControlFrameBodyParser.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/ControlFrameParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/ControlFrameParser.java
index af8b78f..73c0189 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/ControlFrameParser.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/ControlFrameParser.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
@@ -46,6 +43,7 @@ public abstract class ControlFrameParser
parsers.put(ControlFrameType.GO_AWAY, new GoAwayBodyParser(this));
parsers.put(ControlFrameType.HEADERS, new HeadersBodyParser(decompressor, this));
parsers.put(ControlFrameType.WINDOW_UPDATE, new WindowUpdateBodyParser(this));
+ parsers.put(ControlFrameType.CREDENTIAL, new CredentialBodyParser(this));
}
public short getVersion()
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/CredentialBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/CredentialBodyParser.java
new file mode 100644
index 0000000..0dbb3d6
--- /dev/null
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/CredentialBodyParser.java
@@ -0,0 +1,269 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
+
+import java.io.ByteArrayInputStream;
+import java.nio.ByteBuffer;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.jetty.spdy.SessionException;
+import org.eclipse.jetty.spdy.api.SessionStatus;
+import org.eclipse.jetty.spdy.frames.ControlFrameType;
+import org.eclipse.jetty.spdy.frames.CredentialFrame;
+
+public class CredentialBodyParser extends ControlFrameBodyParser
+{
+ private final List<Certificate> certificates = new ArrayList<>();
+ private final ControlFrameParser controlFrameParser;
+ private State state = State.SLOT;
+ private int totalLength;
+ private int cursor;
+ private short slot;
+ private int proofLength;
+ private byte[] proof;
+ private int certificateLength;
+ private byte[] certificate;
+
+ public CredentialBodyParser(ControlFrameParser controlFrameParser)
+ {
+ this.controlFrameParser = controlFrameParser;
+ }
+
+ @Override
+ public boolean parse(ByteBuffer buffer)
+ {
+ while (buffer.hasRemaining())
+ {
+ switch (state)
+ {
+ case SLOT:
+ {
+ if (buffer.remaining() >= 2)
+ {
+ slot = buffer.getShort();
+ checkSlotValid();
+ state = State.PROOF_LENGTH;
+ }
+ else
+ {
+ state = State.SLOT_BYTES;
+ cursor = 2;
+ }
+ break;
+ }
+ case SLOT_BYTES:
+ {
+ byte currByte = buffer.get();
+ --cursor;
+ slot += (currByte & 0xFF) << 8 * cursor;
+ if (cursor == 0)
+ {
+ checkSlotValid();
+ state = State.PROOF_LENGTH;
+ }
+ break;
+ }
+ case PROOF_LENGTH:
+ {
+ if (buffer.remaining() >= 4)
+ {
+ proofLength = buffer.getInt() & 0x7F_FF_FF_FF;
+ state = State.PROOF;
+ }
+ else
+ {
+ state = State.PROOF_LENGTH_BYTES;
+ cursor = 4;
+ }
+ break;
+ }
+ case PROOF_LENGTH_BYTES:
+ {
+ byte currByte = buffer.get();
+ --cursor;
+ proofLength += (currByte & 0xFF) << 8 * cursor;
+ if (cursor == 0)
+ {
+ proofLength &= 0x7F_FF_FF_FF;
+ state = State.PROOF;
+ }
+ break;
+ }
+ case PROOF:
+ {
+ totalLength = controlFrameParser.getLength() - 2 - 4 - proofLength;
+ proof = new byte[proofLength];
+ if (buffer.remaining() >= proofLength)
+ {
+ buffer.get(proof);
+ state = State.CERTIFICATE_LENGTH;
+ if (totalLength == 0)
+ {
+ onCredential();
+ return true;
+ }
+ }
+ else
+ {
+ state = State.PROOF_BYTES;
+ cursor = proofLength;
+ }
+ break;
+ }
+ case PROOF_BYTES:
+ {
+ proof[proofLength - cursor] = buffer.get();
+ --cursor;
+ if (cursor == 0)
+ {
+ state = State.CERTIFICATE_LENGTH;
+ if (totalLength == 0)
+ {
+ onCredential();
+ return true;
+ }
+ }
+ break;
+ }
+ case CERTIFICATE_LENGTH:
+ {
+ if (buffer.remaining() >= 4)
+ {
+ certificateLength = buffer.getInt() & 0x7F_FF_FF_FF;
+ state = State.CERTIFICATE;
+ }
+ else
+ {
+ state = State.CERTIFICATE_LENGTH_BYTES;
+ cursor = 4;
+ }
+ break;
+ }
+ case CERTIFICATE_LENGTH_BYTES:
+ {
+ byte currByte = buffer.get();
+ --cursor;
+ certificateLength += (currByte & 0xFF) << 8 * cursor;
+ if (cursor == 0)
+ {
+ certificateLength &= 0x7F_FF_FF_FF;
+ state = State.CERTIFICATE;
+ }
+ break;
+ }
+ case CERTIFICATE:
+ {
+ totalLength -= 4 + certificateLength;
+ certificate = new byte[certificateLength];
+ if (buffer.remaining() >= certificateLength)
+ {
+ buffer.get(certificate);
+ if (onCertificate())
+ return true;
+ }
+ else
+ {
+ state = State.CERTIFICATE_BYTES;
+ cursor = certificateLength;
+ }
+ break;
+ }
+ case CERTIFICATE_BYTES:
+ {
+ certificate[certificateLength - cursor] = buffer.get();
+ --cursor;
+ if (cursor == 0)
+ {
+ if (onCertificate())
+ return true;
+ }
+ break;
+ }
+ default:
+ {
+ throw new IllegalStateException();
+ }
+ }
+ }
+ return false;
+ }
+
+ private void checkSlotValid()
+ {
+ if (slot <= 0)
+ throw new SessionException(SessionStatus.PROTOCOL_ERROR,
+ "Invalid slot " + slot + " for " + ControlFrameType.CREDENTIAL + " frame");
+ }
+
+ private boolean onCertificate()
+ {
+ certificates.add(deserializeCertificate(certificate));
+ if (totalLength == 0)
+ {
+ onCredential();
+ return true;
+ }
+ else
+ {
+ certificateLength = 0;
+ state = State.CERTIFICATE_LENGTH;
+ }
+ return false;
+ }
+
+ private Certificate deserializeCertificate(byte[] bytes)
+ {
+ try
+ {
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+ return certificateFactory.generateCertificate(new ByteArrayInputStream(bytes));
+ }
+ catch (CertificateException x)
+ {
+ throw new SessionException(SessionStatus.PROTOCOL_ERROR, x);
+ }
+ }
+
+ private void onCredential()
+ {
+ CredentialFrame frame = new CredentialFrame(controlFrameParser.getVersion(), slot,
+ Arrays.copyOf(proof, proof.length), certificates.toArray(new Certificate[certificates.size()]));
+ controlFrameParser.onControlFrame(frame);
+ reset();
+ }
+
+ private void reset()
+ {
+ state = State.SLOT;
+ totalLength = 0;
+ cursor = 0;
+ slot = 0;
+ proofLength = 0;
+ proof = null;
+ certificateLength = 0;
+ certificate = null;
+ certificates.clear();
+ }
+
+ public enum State
+ {
+ SLOT, SLOT_BYTES, PROOF_LENGTH, PROOF_LENGTH_BYTES, PROOF, PROOF_BYTES,
+ CERTIFICATE_LENGTH, CERTIFICATE_LENGTH_BYTES, CERTIFICATE, CERTIFICATE_BYTES
+ }
+}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/DataFrameParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/DataFrameParser.java
index 74ea686..1926ab7 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/DataFrameParser.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/DataFrameParser.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/GoAwayBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/GoAwayBodyParser.java
index 753ad80..cb9fd37 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/GoAwayBodyParser.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/GoAwayBodyParser.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
@@ -24,7 +21,7 @@ import org.eclipse.jetty.spdy.frames.GoAwayFrame;
public class GoAwayBodyParser extends ControlFrameBodyParser
{
private final ControlFrameParser controlFrameParser;
- private State state = State.LAST_STREAM_ID;
+ private State state = State.LAST_GOOD_STREAM_ID;
private int cursor;
private int lastStreamId;
private int statusCode;
@@ -41,7 +38,7 @@ public class GoAwayBodyParser extends ControlFrameBodyParser
{
switch (state)
{
- case LAST_STREAM_ID:
+ case LAST_GOOD_STREAM_ID:
{
if (buffer.remaining() >= 4)
{
@@ -66,12 +63,12 @@ public class GoAwayBodyParser extends ControlFrameBodyParser
}
else
{
- state = State.LAST_STREAM_ID_BYTES;
+ state = State.LAST_GOOD_STREAM_ID_BYTES;
cursor = 4;
}
break;
}
- case LAST_STREAM_ID_BYTES:
+ case LAST_GOOD_STREAM_ID_BYTES:
{
byte currByte = buffer.get();
--cursor;
@@ -144,7 +141,7 @@ public class GoAwayBodyParser extends ControlFrameBodyParser
private void reset()
{
- state = State.LAST_STREAM_ID;
+ state = State.LAST_GOOD_STREAM_ID;
cursor = 0;
lastStreamId = 0;
statusCode = 0;
@@ -152,6 +149,6 @@ public class GoAwayBodyParser extends ControlFrameBodyParser
private enum State
{
- LAST_STREAM_ID, LAST_STREAM_ID_BYTES, STATUS_CODE, STATUS_CODE_BYTES
+ LAST_GOOD_STREAM_ID, LAST_GOOD_STREAM_ID_BYTES, STATUS_CODE, STATUS_CODE_BYTES
}
}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBlockParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBlockParser.java
index 2b4b70b..60725f8 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBlockParser.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBlockParser.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBodyParser.java
index 98bbc7c..a91a19c 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBodyParser.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBodyParser.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
@@ -21,6 +18,7 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.spdy.CompressionFactory;
import org.eclipse.jetty.spdy.api.Headers;
import org.eclipse.jetty.spdy.api.HeadersInfo;
+import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.frames.ControlFrameType;
import org.eclipse.jetty.spdy.frames.HeadersFrame;
@@ -51,7 +49,7 @@ public class HeadersBodyParser extends ControlFrameBodyParser
if (buffer.remaining() >= 4)
{
streamId = buffer.getInt() & 0x7F_FF_FF_FF;
- state = State.HEADERS;
+ state = State.ADDITIONAL;
}
else
{
@@ -68,14 +66,55 @@ public class HeadersBodyParser extends ControlFrameBodyParser
if (cursor == 0)
{
streamId &= 0x7F_FF_FF_FF;
- state = State.HEADERS;
+ state = State.ADDITIONAL;
+ }
+ break;
+ }
+ case ADDITIONAL:
+ {
+ switch (controlFrameParser.getVersion())
+ {
+ case SPDY.V2:
+ {
+ if (buffer.remaining() >= 2)
+ {
+ buffer.getShort();
+ state = State.HEADERS;
+ }
+ else
+ {
+ state = State.ADDITIONAL_BYTES;
+ cursor = 2;
+ }
+ break;
+ }
+ case SPDY.V3:
+ {
+ state = State.HEADERS;
+ break;
+ }
+ default:
+ {
+ throw new IllegalStateException();
+ }
}
break;
}
+ case ADDITIONAL_BYTES:
+ {
+ assert controlFrameParser.getVersion() == SPDY.V2;
+ buffer.get();
+ --cursor;
+ if (cursor == 0)
+ state = State.HEADERS;
+ break;
+ }
case HEADERS:
{
short version = controlFrameParser.getVersion();
int length = controlFrameParser.getLength() - 4;
+ if (version == SPDY.V2)
+ length -= 2;
if (headersBlockParser.parse(streamId, version, length, buffer))
{
byte flags = controlFrameParser.getFlags();
@@ -109,7 +148,7 @@ public class HeadersBodyParser extends ControlFrameBodyParser
private enum State
{
- STREAM_ID, STREAM_ID_BYTES, HEADERS
+ STREAM_ID, STREAM_ID_BYTES, ADDITIONAL, ADDITIONAL_BYTES, HEADERS
}
private class HeadersHeadersBlockParser extends HeadersBlockParser
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/NoOpBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/NoOpBodyParser.java
index ac14c79..f0efe09 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/NoOpBodyParser.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/NoOpBodyParser.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/Parser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/Parser.java
index 8cc42e1..df89760 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/Parser.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/Parser.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/PingBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/PingBodyParser.java
index 3f078f6..0e8ed61 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/PingBodyParser.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/PingBodyParser.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/RstStreamBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/RstStreamBodyParser.java
index 81c9b3c..e05aea4 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/RstStreamBodyParser.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/RstStreamBodyParser.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SettingsBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SettingsBodyParser.java
index d76382a..e4fc7c7 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SettingsBodyParser.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SettingsBodyParser.java
@@ -1,19 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
import java.nio.ByteBuffer;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SynReplyBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SynReplyBodyParser.java
index ec58478..4c93210 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SynReplyBodyParser.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SynReplyBodyParser.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SynStreamBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SynStreamBodyParser.java
index 14673b9..ba54e73 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SynStreamBodyParser.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SynStreamBodyParser.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
@@ -38,6 +35,7 @@ public class SynStreamBodyParser extends ControlFrameBodyParser
private int streamId;
private int associatedStreamId;
private byte priority;
+ private short slot;
public SynStreamBodyParser(CompressionFactory.Decompressor decompressor, ControlFrameParser controlFrameParser)
{
@@ -118,7 +116,9 @@ public class SynStreamBodyParser extends ControlFrameBodyParser
}
else
{
- // Unused byte after priority, skip it
+ slot = (short)(currByte & 0xFF);
+ if (slot < 0)
+ throw new StreamException(streamId, StreamStatus.INVALID_CREDENTIALS);
cursor = 0;
state = State.HEADERS;
}
@@ -134,7 +134,7 @@ public class SynStreamBodyParser extends ControlFrameBodyParser
if (flags > (SynInfo.FLAG_CLOSE | PushSynInfo.FLAG_UNIDIRECTIONAL))
throw new IllegalArgumentException("Invalid flag " + flags + " for frame " + ControlFrameType.SYN_STREAM);
- SynStreamFrame frame = new SynStreamFrame(version, flags, streamId, associatedStreamId, priority, new Headers(headers, true));
+ SynStreamFrame frame = new SynStreamFrame(version, flags, streamId, associatedStreamId, priority, slot, new Headers(headers, true));
controlFrameParser.onControlFrame(frame);
reset();
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/UnknownControlFrameBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/UnknownControlFrameBodyParser.java
index ea5890f..0de0d37 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/UnknownControlFrameBodyParser.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/UnknownControlFrameBodyParser.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/WindowUpdateBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/WindowUpdateBodyParser.java
index 697b885..9010a34 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/WindowUpdateBodyParser.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/WindowUpdateBodyParser.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/AsyncTimeoutTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/AsyncTimeoutTest.java
index a3aab55..16af7c7 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/AsyncTimeoutTest.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/AsyncTimeoutTest.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy;
@@ -23,8 +20,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.io.StandardByteBufferPool;
+import org.eclipse.jetty.spdy.api.Handler;
import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.api.SPDYException;
import org.eclipse.jetty.spdy.api.Session;
@@ -32,7 +28,6 @@ import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.api.StringDataInfo;
import org.eclipse.jetty.spdy.api.SynInfo;
import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.util.Callback;
import org.junit.Assert;
import org.junit.Test;
@@ -48,7 +43,7 @@ public class AsyncTimeoutTest
Executor threadPool = Executors.newCachedThreadPool();
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
- Session session = new StandardSession(SPDY.V2, bufferPool, threadPool, scheduler, new TestController(), null, 1, null, generator)
+ Session session = new StandardSession(SPDY.V2, bufferPool, threadPool, scheduler, new TestController(), null, 1, null, generator, new FlowControlStrategy.None())
{
@Override
public void flush()
@@ -66,7 +61,7 @@ public class AsyncTimeoutTest
};
final CountDownLatch failedLatch = new CountDownLatch(1);
- session.syn(new SynInfo(true), null, timeout, unit, new Callback<Stream>()
+ session.syn(new SynInfo(true), null, timeout, unit, new Handler<Stream>()
{
@Override
public void completed(Stream stream)
@@ -74,7 +69,7 @@ public class AsyncTimeoutTest
}
@Override
- public void failed(Stream context, Throwable x)
+ public void failed(Stream stream, Throwable x)
{
failedLatch.countDown();
}
@@ -93,10 +88,10 @@ public class AsyncTimeoutTest
Executor threadPool = Executors.newCachedThreadPool();
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
- Session session = new StandardSession(SPDY.V2, bufferPool, threadPool, scheduler, new TestController(), null, 1, null, generator)
+ Session session = new StandardSession(SPDY.V2, bufferPool, threadPool, scheduler, new TestController(), null, 1, null, generator, new FlowControlStrategy.None())
{
@Override
- protected void write(ByteBuffer buffer, Callback<FrameBytes> handler, FrameBytes frameBytes)
+ protected void write(ByteBuffer buffer, Handler<FrameBytes> handler, FrameBytes frameBytes)
{
try
{
@@ -114,7 +109,7 @@ public class AsyncTimeoutTest
Stream stream = session.syn(new SynInfo(false), null).get(5, TimeUnit.SECONDS);
final CountDownLatch failedLatch = new CountDownLatch(1);
- stream.data(new StringDataInfo("data", true), timeout, unit, new Callback<Void>()
+ stream.data(new StringDataInfo("data", true), timeout, unit, new Handler<Void>()
{
@Override
public void completed(Void context)
@@ -134,9 +129,9 @@ public class AsyncTimeoutTest
private static class TestController implements Controller<StandardSession.FrameBytes>
{
@Override
- public int write(ByteBuffer buffer, Callback<StandardSession.FrameBytes> callback, StandardSession.FrameBytes context)
+ public int write(ByteBuffer buffer, Handler<StandardSession.FrameBytes> handler, StandardSession.FrameBytes context)
{
- callback.completed(context);
+ handler.completed(context);
return buffer.remaining();
}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/StandardSessionTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/StandardSessionTest.java
index a7dd98a..6406f62 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/StandardSessionTest.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/StandardSessionTest.java
@@ -1,22 +1,20 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy;
import java.nio.ByteBuffer;
+import java.nio.channels.ClosedChannelException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
@@ -25,9 +23,10 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.io.StandardByteBufferPool;
+import org.eclipse.jetty.spdy.StandardSession.FrameBytes;
+import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
import org.eclipse.jetty.spdy.api.DataInfo;
+import org.eclipse.jetty.spdy.api.Handler;
import org.eclipse.jetty.spdy.api.Headers;
import org.eclipse.jetty.spdy.api.HeadersInfo;
import org.eclipse.jetty.spdy.api.RstInfo;
@@ -42,28 +41,30 @@ import org.eclipse.jetty.spdy.frames.DataFrame;
import org.eclipse.jetty.spdy.frames.SynReplyFrame;
import org.eclipse.jetty.spdy.frames.SynStreamFrame;
import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.util.Callback;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
+import org.mockito.stubbing.Answer;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class StandardSessionTest
{
@Mock
- private ISession sessionMock;
+ private Controller<FrameBytes> controller;
+
private ByteBufferPool bufferPool;
private Executor threadPool;
private StandardSession session;
@@ -78,13 +79,36 @@ public class StandardSessionTest
threadPool = Executors.newCachedThreadPool();
scheduler = Executors.newSingleThreadScheduledExecutor();
generator = new Generator(new StandardByteBufferPool(),new StandardCompressionFactory.StandardCompressor());
- session = new StandardSession(SPDY.V2,bufferPool,threadPool,scheduler,new TestController(),null,1,null,generator);
+ session = new StandardSession(SPDY.V2,bufferPool,threadPool,scheduler,controller,null,1,null,generator,new FlowControlStrategy.None());
headers = new Headers();
}
+ @SuppressWarnings("unchecked")
+ private void setControllerWriteExpectationToFail(final boolean fail)
+ {
+ when(controller.write(any(ByteBuffer.class),any(Handler.class),any(StandardSession.FrameBytes.class))).thenAnswer(new Answer<Integer>()
+ {
+ public Integer answer(InvocationOnMock invocation)
+ {
+ Object[] args = invocation.getArguments();
+
+ Handler<StandardSession.FrameBytes> handler = (Handler<FrameBytes>)args[1];
+ FrameBytes context = (FrameBytes)args[2];
+
+ if (fail)
+ handler.failed(context,new ClosedChannelException());
+ else
+ handler.completed(context);
+ return 0;
+ }
+ });
+ }
+
@Test
public void testStreamIsRemovedFromSessionWhenReset() throws InterruptedException, ExecutionException, TimeoutException
{
+ setControllerWriteExpectationToFail(false);
+
IStream stream = createStream();
assertThatStreamIsInSession(stream);
assertThat("stream is not reset",stream.isReset(),is(false));
@@ -96,6 +120,8 @@ public class StandardSessionTest
@Test
public void testStreamIsAddedAndRemovedFromSession() throws InterruptedException, ExecutionException, TimeoutException
{
+ setControllerWriteExpectationToFail(false);
+
IStream stream = createStream();
assertThatStreamIsInSession(stream);
stream.updateCloseState(true,true);
@@ -107,6 +133,8 @@ public class StandardSessionTest
@Test
public void testStreamIsRemovedWhenHeadersWithCloseFlagAreSent() throws InterruptedException, ExecutionException, TimeoutException
{
+ setControllerWriteExpectationToFail(false);
+
IStream stream = createStream();
assertThatStreamIsInSession(stream);
stream.updateCloseState(true,false);
@@ -118,6 +146,8 @@ public class StandardSessionTest
@Test
public void testStreamIsUnidirectional() throws InterruptedException, ExecutionException, TimeoutException
{
+ setControllerWriteExpectationToFail(false);
+
IStream stream = createStream();
assertThat("stream is not unidirectional",stream.isUnidirectional(),not(true));
Stream pushStream = createPushStream(stream);
@@ -127,6 +157,8 @@ public class StandardSessionTest
@Test
public void testPushStreamCreation() throws InterruptedException, ExecutionException, TimeoutException
{
+ setControllerWriteExpectationToFail(false);
+
Stream stream = createStream();
IStream pushStream = createPushStream(stream);
assertThat("Push stream must be associated to the first stream created",pushStream.getAssociatedStream().getId(),is(stream.getId()));
@@ -136,6 +168,8 @@ public class StandardSessionTest
@Test
public void testPushStreamIsNotClosedWhenAssociatedStreamIsClosed() throws InterruptedException, ExecutionException, TimeoutException
{
+ setControllerWriteExpectationToFail(false);
+
IStream stream = createStream();
Stream pushStream = createPushStream(stream);
assertThatStreamIsNotHalfClosed(stream);
@@ -157,6 +191,8 @@ public class StandardSessionTest
@Test
public void testCreatePushStreamOnClosedStream() throws InterruptedException, ExecutionException, TimeoutException
{
+ setControllerWriteExpectationToFail(false);
+
IStream stream = createStream();
stream.updateCloseState(true,true);
assertThatStreamIsHalfClosed(stream);
@@ -169,15 +205,10 @@ public class StandardSessionTest
{
final CountDownLatch failedLatch = new CountDownLatch(1);
SynInfo synInfo = new SynInfo(headers,false,stream.getPriority());
- stream.syn(synInfo,5,TimeUnit.SECONDS,new Callback<Stream>()
+ stream.syn(synInfo,5,TimeUnit.SECONDS,new Handler.Adapter<Stream>()
{
@Override
- public void completed(Stream context)
- {
- }
-
- @Override
- public void failed(Stream context, Throwable x)
+ public void failed(Stream stream, Throwable x)
{
failedLatch.countDown();
}
@@ -188,6 +219,8 @@ public class StandardSessionTest
@Test
public void testPushStreamIsAddedAndRemovedFromParentAndSessionWhenClosed() throws InterruptedException, ExecutionException, TimeoutException
{
+ setControllerWriteExpectationToFail(false);
+
IStream stream = createStream();
IStream pushStream = createPushStream(stream);
assertThatPushStreamIsHalfClosed(pushStream);
@@ -202,6 +235,8 @@ public class StandardSessionTest
@Test
public void testPushStreamIsRemovedWhenReset() throws InterruptedException, ExecutionException, TimeoutException
{
+ setControllerWriteExpectationToFail(false);
+
IStream stream = createStream();
IStream pushStream = (IStream)stream.syn(new SynInfo(false)).get();
assertThatPushStreamIsInSession(pushStream);
@@ -214,6 +249,8 @@ public class StandardSessionTest
@Test
public void testPushStreamWithSynInfoClosedTrue() throws InterruptedException, ExecutionException, TimeoutException
{
+ setControllerWriteExpectationToFail(false);
+
IStream stream = createStream();
SynInfo synInfo = new SynInfo(headers,true,stream.getPriority());
IStream pushStream = (IStream)stream.syn(synInfo).get(5,TimeUnit.SECONDS);
@@ -227,6 +264,8 @@ public class StandardSessionTest
public void testPushStreamSendHeadersWithCloseFlagIsRemovedFromSessionAndDisassociateFromParent() throws InterruptedException, ExecutionException,
TimeoutException
{
+ setControllerWriteExpectationToFail(false);
+
IStream stream = createStream();
SynInfo synInfo = new SynInfo(headers,false,stream.getPriority());
IStream pushStream = (IStream)stream.syn(synInfo).get(5,TimeUnit.SECONDS);
@@ -242,6 +281,8 @@ public class StandardSessionTest
@Test
public void testCreatedAndClosedListenersAreCalledForNewStream() throws InterruptedException, ExecutionException, TimeoutException
{
+ setControllerWriteExpectationToFail(false);
+
final CountDownLatch createdListenerCalledLatch = new CountDownLatch(1);
final CountDownLatch closedListenerCalledLatch = new CountDownLatch(1);
session.addListener(new TestStreamListener(createdListenerCalledLatch,closedListenerCalledLatch));
@@ -255,6 +296,8 @@ public class StandardSessionTest
@Test
public void testListenerIsCalledForResetStream() throws InterruptedException, ExecutionException, TimeoutException
{
+ setControllerWriteExpectationToFail(false);
+
final CountDownLatch closedListenerCalledLatch = new CountDownLatch(1);
session.addListener(new TestStreamListener(null,closedListenerCalledLatch));
IStream stream = createStream();
@@ -265,6 +308,8 @@ public class StandardSessionTest
@Test
public void testCreatedAndClosedListenersAreCalledForNewPushStream() throws InterruptedException, ExecutionException, TimeoutException
{
+ setControllerWriteExpectationToFail(false);
+
final CountDownLatch createdListenerCalledLatch = new CountDownLatch(2);
final CountDownLatch closedListenerCalledLatch = new CountDownLatch(1);
session.addListener(new TestStreamListener(createdListenerCalledLatch,closedListenerCalledLatch));
@@ -279,6 +324,8 @@ public class StandardSessionTest
@Test
public void testListenerIsCalledForResetPushStream() throws InterruptedException, ExecutionException, TimeoutException
{
+ setControllerWriteExpectationToFail(false);
+
final CountDownLatch closedListenerCalledLatch = new CountDownLatch(1);
session.addListener(new TestStreamListener(null,closedListenerCalledLatch));
IStream stream = createStream();
@@ -315,31 +362,23 @@ public class StandardSessionTest
}
}
- @SuppressWarnings("unchecked")
- @Test(expected = IllegalStateException.class)
- public void testSendDataOnHalfClosedStream() throws InterruptedException, ExecutionException, TimeoutException
- {
- SynStreamFrame synStreamFrame = new SynStreamFrame(SPDY.V2,SynInfo.FLAG_CLOSE,1,0,(byte)0,null);
- IStream stream = new StandardStream(synStreamFrame,sessionMock,8184,null);
- stream.updateCloseState(synStreamFrame.isClose(),true);
- assertThat("stream is half closed",stream.isHalfClosed(),is(true));
- stream.data(new StringDataInfo("data on half closed stream",true));
- verify(sessionMock,never()).data(any(IStream.class),any(DataInfo.class),anyInt(),any(TimeUnit.class),any(Callback.class),any(void.class));
- }
-
@Test
@Ignore("In V3 we need to rst the stream if we receive data on a remotely half closed stream.")
public void receiveDataOnRemotelyHalfClosedStreamResetsStreamInV3() throws InterruptedException, ExecutionException
{
+ setControllerWriteExpectationToFail(false);
+
IStream stream = (IStream)session.syn(new SynInfo(false),new StreamFrameListener.Adapter()).get();
stream.updateCloseState(true,false);
assertThat("stream is half closed from remote side",stream.isHalfClosed(),is(true));
- stream.process(new DataFrame(stream.getId(),(byte)0,256),ByteBuffer.allocate(256));
+ stream.process(new ByteBufferDataInfo(ByteBuffer.allocate(256), true));
}
@Test
public void testReceiveDataOnRemotelyClosedStreamIsIgnored() throws InterruptedException, ExecutionException, TimeoutException
{
+ setControllerWriteExpectationToFail(false);
+
final CountDownLatch onDataCalledLatch = new CountDownLatch(1);
Stream stream = session.syn(new SynInfo(false),new StreamFrameListener.Adapter()
{
@@ -355,10 +394,38 @@ public class StandardSessionTest
assertThat("onData is never called",onDataCalledLatch.await(1,TimeUnit.SECONDS),not(true));
}
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testControllerWriteFailsInEndPointFlush() throws InterruptedException
+ {
+ setControllerWriteExpectationToFail(true);
+
+ final CountDownLatch failedCalledLatch = new CountDownLatch(2);
+ SynStreamFrame synStreamFrame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)0, (short)0, null);
+ IStream stream = new StandardStream(synStreamFrame.getStreamId(), synStreamFrame.getPriority(), session, null);
+ stream.updateWindowSize(8192);
+ Handler.Adapter<Void> handler = new Handler.Adapter<Void>()
+ {
+ @Override
+ public void failed(Void context, Throwable x)
+ {
+ failedCalledLatch.countDown();
+ }
+ };
+
+ // first data frame should fail on controller.write()
+ stream.data(new StringDataInfo("data", false), 5, TimeUnit.SECONDS, handler);
+ // second data frame should fail without controller.writer() as the connection is expected to be broken after first controller.write() call failed.
+ stream.data(new StringDataInfo("data", false), 5, TimeUnit.SECONDS, handler);
+
+ verify(controller, times(1)).write(any(ByteBuffer.class), any(Handler.class), any(FrameBytes.class));
+ assertThat("Handler.failed has been called twice", failedCalledLatch.await(5, TimeUnit.SECONDS), is(true));
+ }
+
private IStream createStream() throws InterruptedException, ExecutionException, TimeoutException
{
SynInfo synInfo = new SynInfo(headers,false,(byte)0);
- return (IStream)session.syn(synInfo,new StreamFrameListener.Adapter()).get(5,TimeUnit.SECONDS);
+ return (IStream)session.syn(synInfo,new StreamFrameListener.Adapter()).get(50,TimeUnit.SECONDS);
}
private IStream createPushStream(Stream stream) throws InterruptedException, ExecutionException, TimeoutException
@@ -367,21 +434,6 @@ public class StandardSessionTest
return (IStream)stream.syn(synInfo).get(5,TimeUnit.SECONDS);
}
- private static class TestController implements Controller<StandardSession.FrameBytes>
- {
- @Override
- public int write(ByteBuffer buffer, Callback<StandardSession.FrameBytes> callback, StandardSession.FrameBytes context)
- {
- callback.completed(context);
- return buffer.remaining();
- }
-
- @Override
- public void close(boolean onlyOutput)
- {
- }
- }
-
private void assertThatStreamIsClosed(IStream stream)
{
assertThat("stream is closed",stream.isClosed(),is(true));
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/StandardStreamTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/StandardStreamTest.java
index 7ab08ab..f75c723 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/StandardStreamTest.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/StandardStreamTest.java
@@ -1,29 +1,33 @@
-// ========================================================================
-// Copyright (c) 2009-2009 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.
-// ========================================================================
-
+//========================================================================
+//Copyright 2011-2012 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.spdy;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.eclipse.jetty.spdy.api.DataInfo;
+import org.eclipse.jetty.spdy.api.Handler;
+import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.api.StreamFrameListener;
+import org.eclipse.jetty.spdy.api.StringDataInfo;
import org.eclipse.jetty.spdy.api.SynInfo;
import org.eclipse.jetty.spdy.frames.SynStreamFrame;
-import org.eclipse.jetty.util.Callback;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
@@ -34,20 +38,20 @@ import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-
-/* ------------------------------------------------------------ */
-/**
- */
@RunWith(MockitoJUnitRunner.class)
public class StandardStreamTest
{
- @Mock private ISession session;
- @Mock private SynStreamFrame synStreamFrame;
+ @Mock
+ private ISession session;
+ @Mock
+ private SynStreamFrame synStreamFrame;
/**
* Test method for {@link org.eclipse.jetty.spdy.StandardStream#syn(org.eclipse.jetty.spdy.api.SynInfo)}.
@@ -56,17 +60,18 @@ public class StandardStreamTest
@Test
public void testSyn()
{
- Stream stream = new StandardStream(synStreamFrame,session,0,null);
+ Stream stream = new StandardStream(synStreamFrame.getStreamId(), synStreamFrame.getPriority(), session, null);
Set<Stream> streams = new HashSet<>();
streams.add(stream);
when(synStreamFrame.isClose()).thenReturn(false);
SynInfo synInfo = new SynInfo(false);
when(session.getStreams()).thenReturn(streams);
stream.syn(synInfo);
- verify(session).syn(argThat(new PushSynInfoMatcher(stream.getId(),synInfo)),any(StreamFrameListener.class),anyLong(),any(TimeUnit.class),any(Callback.class));
+ verify(session).syn(argThat(new PushSynInfoMatcher(stream.getId(), synInfo)), any(StreamFrameListener.class), anyLong(), any(TimeUnit.class), any(Handler.class));
}
- private class PushSynInfoMatcher extends ArgumentMatcher<PushSynInfo>{
+ private class PushSynInfoMatcher extends ArgumentMatcher<PushSynInfo>
+ {
int associatedStreamId;
SynInfo synInfo;
@@ -75,15 +80,18 @@ public class StandardStreamTest
this.associatedStreamId = associatedStreamId;
this.synInfo = synInfo;
}
+
@Override
public boolean matches(Object argument)
{
PushSynInfo pushSynInfo = (PushSynInfo)argument;
- if(pushSynInfo.getAssociatedStreamId() != associatedStreamId){
+ if (pushSynInfo.getAssociatedStreamId() != associatedStreamId)
+ {
System.out.println("streamIds do not match!");
return false;
}
- if(pushSynInfo.isClose() != synInfo.isClose()){
+ if (pushSynInfo.isClose() != synInfo.isClose())
+ {
System.out.println("isClose doesn't match");
return false;
}
@@ -92,16 +100,17 @@ public class StandardStreamTest
}
@Test
- public void testSynOnClosedStream(){
- IStream stream = new StandardStream(synStreamFrame,session,0,null);
- stream.updateCloseState(true,true);
- stream.updateCloseState(true,false);
- assertThat("stream expected to be closed",stream.isClosed(),is(true));
+ public void testSynOnClosedStream()
+ {
+ IStream stream = new StandardStream(synStreamFrame.getStreamId(), synStreamFrame.getPriority(), session, null);
+ stream.updateCloseState(true, true);
+ stream.updateCloseState(true, false);
+ assertThat("stream expected to be closed", stream.isClosed(), is(true));
final CountDownLatch failedLatch = new CountDownLatch(1);
- stream.syn(new SynInfo(false),1,TimeUnit.SECONDS,new Callback.Adapter<Stream>()
+ stream.syn(new SynInfo(false), 1, TimeUnit.SECONDS, new Handler.Adapter<Stream>()
{
@Override
- public void failed(Stream context, Throwable x)
+ public void failed(Stream stream, Throwable x)
{
failedLatch.countDown();
}
@@ -109,4 +118,16 @@ public class StandardStreamTest
assertThat("PushStream creation failed", failedLatch.getCount(), equalTo(0L));
}
+ @SuppressWarnings("unchecked")
+ @Test(expected = IllegalStateException.class)
+ public void testSendDataOnHalfClosedStream() throws InterruptedException, ExecutionException, TimeoutException
+ {
+ SynStreamFrame synStreamFrame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)0, (short)0, null);
+ IStream stream = new StandardStream(synStreamFrame.getStreamId(), synStreamFrame.getPriority(), session, null);
+ stream.updateWindowSize(8192);
+ stream.updateCloseState(synStreamFrame.isClose(), true);
+ assertThat("stream is half closed", stream.isHalfClosed(), is(true));
+ stream.data(new StringDataInfo("data on half closed stream", true));
+ verify(session, never()).data(any(IStream.class), any(DataInfo.class), anyInt(), any(TimeUnit.class), any(Handler.class), any(void.class));
+ }
}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ClientUsageTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ClientUsageTest.java
index 127e72b..9de7f49 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ClientUsageTest.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ClientUsageTest.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
@@ -20,7 +17,6 @@ import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.spdy.StandardSession;
-import org.eclipse.jetty.util.Callback;
import org.junit.Ignore;
import org.junit.Test;
@@ -30,7 +26,7 @@ public class ClientUsageTest
@Test
public void testClientRequestResponseNoBody() throws Exception
{
- Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null);
+ Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null, null);
session.syn(new SynInfo(true), new StreamFrameListener.Adapter()
{
@@ -49,7 +45,7 @@ public class ClientUsageTest
@Test
public void testClientRequestWithBodyResponseNoBody() throws Exception
{
- Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null);
+ Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null, null);
Stream stream = session.syn(new SynInfo(false), new StreamFrameListener.Adapter()
{
@@ -70,7 +66,7 @@ public class ClientUsageTest
@Test
public void testAsyncClientRequestWithBodyResponseNoBody() throws Exception
{
- Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null);
+ Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null, null);
final String context = "context";
session.syn(new SynInfo(false), new StreamFrameListener.Adapter()
@@ -84,7 +80,7 @@ public class ClientUsageTest
// Then issue another similar request
stream.getSession().syn(new SynInfo(true), this);
}
- }, 0, TimeUnit.MILLISECONDS, new Callback.Adapter<Stream>()
+ }, 0, TimeUnit.MILLISECONDS, new Handler.Adapter<Stream>()
{
@Override
public void completed(Stream stream)
@@ -105,7 +101,7 @@ public class ClientUsageTest
@Test
public void testAsyncClientRequestWithBodyAndResponseWithBody() throws Exception
{
- Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null);
+ Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null, null);
session.syn(new SynInfo(false), new StreamFrameListener.Adapter()
{
@@ -139,7 +135,7 @@ public class ClientUsageTest
}
}
- }, 0, TimeUnit.MILLISECONDS, new Callback.Adapter<Stream>()
+ }, 0, TimeUnit.MILLISECONDS, new Handler.Adapter<Stream>()
{
@Override
public void completed(Stream stream)
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ServerUsageTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ServerUsageTest.java
index d74fae2..b1a326f 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ServerUsageTest.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ServerUsageTest.java
@@ -1,25 +1,21 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.api;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.util.Callback;
import org.junit.Ignore;
import org.junit.Test;
@@ -70,7 +66,7 @@ public class ServerUsageTest
//
// However, the API may allow to initiate the stream
- session.syn(new SynInfo(false), null, 0, TimeUnit.MILLISECONDS, new Callback.Adapter<Stream>()
+ session.syn(new SynInfo(false), null, 0, TimeUnit.MILLISECONDS, new Handler.Adapter<Stream>()
{
@Override
public void completed(Stream stream)
@@ -100,7 +96,7 @@ public class ServerUsageTest
Session session = stream.getSession();
// Since it's unidirectional, no need to pass the listener
- session.syn(new SynInfo(new Headers(), false, (byte)0), null, 0, TimeUnit.MILLISECONDS, new Callback.Adapter<Stream>()
+ session.syn(new SynInfo(new Headers(), false, (byte)0), null, 0, TimeUnit.MILLISECONDS, new Handler.Adapter<Stream>()
{
@Override
public void completed(Stream pushStream)
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/CredentialGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/CredentialGenerateParseTest.java
new file mode 100644
index 0000000..e7db2c8
--- /dev/null
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/CredentialGenerateParseTest.java
@@ -0,0 +1,99 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
+
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
+
+import org.eclipse.jetty.spdy.StandardByteBufferPool;
+import org.eclipse.jetty.spdy.StandardCompressionFactory;
+import org.eclipse.jetty.spdy.api.SPDY;
+import org.eclipse.jetty.spdy.generator.Generator;
+import org.eclipse.jetty.spdy.parser.Parser;
+import org.eclipse.jetty.util.resource.Resource;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class CredentialGenerateParseTest
+{
+ @Test
+ public void testGenerateParse() throws Exception
+ {
+ short slot = 1;
+ byte[] proof = new byte[]{0, 1, 2};
+ Certificate[] temp = loadCertificates();
+ Certificate[] certificates = new Certificate[temp.length * 2];
+ System.arraycopy(temp, 0, certificates, 0, temp.length);
+ System.arraycopy(temp, 0, certificates, temp.length, temp.length);
+ CredentialFrame frame1 = new CredentialFrame(SPDY.V3, slot, proof, certificates);
+ Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory().newCompressor());
+ ByteBuffer buffer = generator.control(frame1);
+
+ Assert.assertNotNull(buffer);
+
+ TestSPDYParserListener listener = new TestSPDYParserListener();
+ Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
+ parser.addListener(listener);
+ parser.parse(buffer);
+ ControlFrame frame2 = listener.getControlFrame();
+
+ Assert.assertNotNull(frame2);
+ Assert.assertEquals(ControlFrameType.CREDENTIAL, frame2.getType());
+ CredentialFrame credential = (CredentialFrame)frame2;
+ Assert.assertEquals(SPDY.V3, credential.getVersion());
+ Assert.assertEquals(0, credential.getFlags());
+ Assert.assertEquals(slot, credential.getSlot());
+ Assert.assertArrayEquals(proof, credential.getProof());
+ Assert.assertArrayEquals(certificates, credential.getCertificateChain());
+ }
+
+ @Test
+ public void testGenerateParseOneByteAtATime() throws Exception
+ {
+ short slot = 1;
+ byte[] proof = new byte[]{0, 1, 2};
+ Certificate[] certificates = loadCertificates();
+ CredentialFrame frame1 = new CredentialFrame(SPDY.V3, slot, proof, certificates);
+ Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory().newCompressor());
+ ByteBuffer buffer = generator.control(frame1);
+
+ Assert.assertNotNull(buffer);
+
+ TestSPDYParserListener listener = new TestSPDYParserListener();
+ Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
+ parser.addListener(listener);
+ while (buffer.hasRemaining())
+ parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
+ ControlFrame frame2 = listener.getControlFrame();
+
+ Assert.assertNotNull(frame2);
+ Assert.assertEquals(ControlFrameType.CREDENTIAL, frame2.getType());
+ CredentialFrame credential = (CredentialFrame)frame2;
+ Assert.assertEquals(SPDY.V3, credential.getVersion());
+ Assert.assertEquals(0, credential.getFlags());
+ Assert.assertEquals(slot, credential.getSlot());
+ Assert.assertArrayEquals(proof, credential.getProof());
+ Assert.assertArrayEquals(certificates, credential.getCertificateChain());
+ }
+
+ private Certificate[] loadCertificates() throws Exception
+ {
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ InputStream keyStoreStream = Resource.newResource("src/test/resources/keystore.jks").getInputStream();
+ keyStore.load(keyStoreStream, "storepwd".toCharArray());
+ return keyStore.getCertificateChain("mykey");
+ }
+}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/DataGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/DataGenerateParseTest.java
index f31f28f..6f53b64 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/DataGenerateParseTest.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/DataGenerateParseTest.java
@@ -1,24 +1,21 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.StandardByteBufferPool;
+import org.eclipse.jetty.spdy.StandardByteBufferPool;
import org.eclipse.jetty.spdy.StandardCompressionFactory;
import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.StringDataInfo;
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/GoAwayGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/GoAwayGenerateParseTest.java
index 91d197e..c3ae26d 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/GoAwayGenerateParseTest.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/GoAwayGenerateParseTest.java
@@ -1,24 +1,21 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.StandardByteBufferPool;
+import org.eclipse.jetty.spdy.StandardByteBufferPool;
import org.eclipse.jetty.spdy.StandardCompressionFactory;
import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.generator.Generator;
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/HeadersGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/HeadersGenerateParseTest.java
index b8c6277..fd5a36d 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/HeadersGenerateParseTest.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/HeadersGenerateParseTest.java
@@ -1,89 +1,98 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.StandardByteBufferPool;
+import org.eclipse.jetty.spdy.StandardByteBufferPool;
import org.eclipse.jetty.spdy.StandardCompressionFactory;
import org.eclipse.jetty.spdy.api.Headers;
import org.eclipse.jetty.spdy.api.HeadersInfo;
import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.generator.Generator;
import org.eclipse.jetty.spdy.parser.Parser;
-import org.junit.Assert;
+import org.junit.Before;
import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+
public class HeadersGenerateParseTest
{
- @Test
- public void testGenerateParse() throws Exception
+
+ private Headers headers = new Headers();
+ private int streamId = 13;
+ private byte flags = HeadersInfo.FLAG_RESET_COMPRESSION;
+ private final TestSPDYParserListener listener = new TestSPDYParserListener();
+ private final Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
+ private ByteBuffer buffer;
+
+ @Before
+ public void setUp()
{
- byte flags = HeadersInfo.FLAG_RESET_COMPRESSION;
- int streamId = 13;
- Headers headers = new Headers();
+ parser.addListener(listener);
headers.put("a", "b");
+ buffer = createHeadersFrameBuffer(headers);
+ }
+
+ private ByteBuffer createHeadersFrameBuffer(Headers headers)
+ {
HeadersFrame frame1 = new HeadersFrame(SPDY.V2, flags, streamId, headers);
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory().newCompressor());
ByteBuffer buffer = generator.control(frame1);
+ assertThat("Buffer is not null", buffer, notNullValue());
+ return buffer;
+ }
- Assert.assertNotNull(buffer);
-
- TestSPDYParserListener listener = new TestSPDYParserListener();
- Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
- parser.addListener(listener);
+ @Test
+ public void testGenerateParse() throws Exception
+ {
parser.parse(buffer);
- ControlFrame frame2 = listener.getControlFrame();
-
- Assert.assertNotNull(frame2);
- Assert.assertEquals(ControlFrameType.HEADERS, frame2.getType());
- HeadersFrame headersFrame = (HeadersFrame)frame2;
- Assert.assertEquals(SPDY.V2, headersFrame.getVersion());
- Assert.assertEquals(streamId, headersFrame.getStreamId());
- Assert.assertEquals(flags, headersFrame.getFlags());
- Assert.assertEquals(headers, headersFrame.getHeaders());
+ assertExpectationsAreMet(headers);
}
@Test
public void testGenerateParseOneByteAtATime() throws Exception
{
- byte flags = HeadersInfo.FLAG_RESET_COMPRESSION;
- int streamId = 13;
- Headers headers = new Headers();
- headers.put("a", "b");
- HeadersFrame frame1 = new HeadersFrame(SPDY.V2, flags, streamId, headers);
- Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory().newCompressor());
- ByteBuffer buffer = generator.control(frame1);
-
- Assert.assertNotNull(buffer);
-
- TestSPDYParserListener listener = new TestSPDYParserListener();
- Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
- parser.addListener(listener);
while (buffer.hasRemaining())
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
- ControlFrame frame2 = listener.getControlFrame();
- Assert.assertNotNull(frame2);
- Assert.assertEquals(ControlFrameType.HEADERS, frame2.getType());
- HeadersFrame headersFrame = (HeadersFrame)frame2;
- Assert.assertEquals(SPDY.V2, headersFrame.getVersion());
- Assert.assertEquals(streamId, headersFrame.getStreamId());
- Assert.assertEquals(flags, headersFrame.getFlags());
- Assert.assertEquals(headers, headersFrame.getHeaders());
+ assertExpectationsAreMet(headers);
+ }
+
+ @Test
+ public void testHeadersAreTranslatedToLowerCase()
+ {
+ Headers headers = new Headers();
+ headers.put("Via","localhost");
+ parser.parse(createHeadersFrameBuffer(headers));
+ HeadersFrame parsedHeadersFrame = assertExpectationsAreMet(headers);
+ Headers.Header viaHeader = parsedHeadersFrame.getHeaders().get("via");
+ assertThat("Via Header name is lowercase", viaHeader.name(), is("via"));
+ }
+
+ private HeadersFrame assertExpectationsAreMet(Headers headers)
+ {
+ ControlFrame parsedControlFrame = listener.getControlFrame();
+ assertThat("listener received controlFrame", parsedControlFrame, notNullValue());
+ assertThat("ControlFrame type is HEADERS", ControlFrameType.HEADERS, is(parsedControlFrame.getType()));
+ HeadersFrame headersFrame = (HeadersFrame)parsedControlFrame;
+ assertThat("Version matches", SPDY.V2, is(headersFrame.getVersion()));
+ assertThat("StreamId matches", streamId, is(headersFrame.getStreamId()));
+ assertThat("flags match", flags, is(headersFrame.getFlags()));
+ assertThat("headers match", headers, is(headersFrame.getHeaders()));
+ return headersFrame;
}
}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/NoOpGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/NoOpGenerateParseTest.java
index 7519e17..9c248be 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/NoOpGenerateParseTest.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/NoOpGenerateParseTest.java
@@ -1,24 +1,21 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.StandardByteBufferPool;
+import org.eclipse.jetty.spdy.StandardByteBufferPool;
import org.eclipse.jetty.spdy.StandardCompressionFactory;
import org.eclipse.jetty.spdy.generator.Generator;
import org.eclipse.jetty.spdy.parser.Parser;
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/PingGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/PingGenerateParseTest.java
index b287b1d..1bd87f8 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/PingGenerateParseTest.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/PingGenerateParseTest.java
@@ -1,24 +1,21 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.StandardByteBufferPool;
+import org.eclipse.jetty.spdy.StandardByteBufferPool;
import org.eclipse.jetty.spdy.StandardCompressionFactory;
import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.generator.Generator;
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/RstStreamGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/RstStreamGenerateParseTest.java
index fcceab3..df70d36 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/RstStreamGenerateParseTest.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/RstStreamGenerateParseTest.java
@@ -1,24 +1,27 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.StandardByteBufferPool;
+import org.eclipse.jetty.spdy.StandardByteBufferPool;
import org.eclipse.jetty.spdy.StandardCompressionFactory;
import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.api.StreamStatus;
@@ -27,12 +30,6 @@ import org.eclipse.jetty.spdy.parser.Parser;
import org.junit.Assert;
import org.junit.Test;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.Matchers.nullValue;
-import static org.junit.Assert.assertThat;
-
public class RstStreamGenerateParseTest
{
@Test
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SettingsGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SettingsGenerateParseTest.java
index 2843780..88b640d 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SettingsGenerateParseTest.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SettingsGenerateParseTest.java
@@ -1,24 +1,21 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.StandardByteBufferPool;
+import org.eclipse.jetty.spdy.StandardByteBufferPool;
import org.eclipse.jetty.spdy.StandardCompressionFactory;
import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.api.Settings;
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SynReplyGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SynReplyGenerateParseTest.java
index 4ca4934..6da3189 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SynReplyGenerateParseTest.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SynReplyGenerateParseTest.java
@@ -1,24 +1,21 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.StandardByteBufferPool;
+import org.eclipse.jetty.spdy.StandardByteBufferPool;
import org.eclipse.jetty.spdy.StandardCompressionFactory;
import org.eclipse.jetty.spdy.api.Headers;
import org.eclipse.jetty.spdy.api.ReplyInfo;
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SynStreamGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SynStreamGenerateParseTest.java
index d952766..5cf4b22 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SynStreamGenerateParseTest.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SynStreamGenerateParseTest.java
@@ -1,24 +1,21 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.StandardByteBufferPool;
+import org.eclipse.jetty.spdy.StandardByteBufferPool;
import org.eclipse.jetty.spdy.StandardCompressionFactory;
import org.eclipse.jetty.spdy.api.Headers;
import org.eclipse.jetty.spdy.api.SPDY;
@@ -37,10 +34,11 @@ public class SynStreamGenerateParseTest
int streamId = 13;
int associatedStreamId = 11;
byte priority = 3;
+ short slot = 5;
Headers headers = new Headers();
headers.put("a", "b");
headers.put("c", "d");
- SynStreamFrame frame1 = new SynStreamFrame(SPDY.V2, flags, streamId, associatedStreamId, priority, headers);
+ SynStreamFrame frame1 = new SynStreamFrame(SPDY.V2, flags, streamId, associatedStreamId, priority, slot, headers);
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory().newCompressor());
ByteBuffer buffer = generator.control(frame1);
@@ -60,6 +58,7 @@ public class SynStreamGenerateParseTest
Assert.assertEquals(associatedStreamId, synStream.getAssociatedStreamId());
Assert.assertEquals(flags, synStream.getFlags());
Assert.assertEquals(priority, synStream.getPriority());
+ Assert.assertEquals(slot, synStream.getSlot());
Assert.assertEquals(headers, synStream.getHeaders());
}
@@ -70,10 +69,11 @@ public class SynStreamGenerateParseTest
int streamId = 13;
int associatedStreamId = 11;
byte priority = 3;
+ short slot = 5;
Headers headers = new Headers();
headers.put("a", "b");
headers.put("c", "d");
- SynStreamFrame frame1 = new SynStreamFrame(SPDY.V2, flags, streamId, associatedStreamId, priority, headers);
+ SynStreamFrame frame1 = new SynStreamFrame(SPDY.V2, flags, streamId, associatedStreamId, priority, slot, headers);
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory().newCompressor());
ByteBuffer buffer = generator.control(frame1);
@@ -94,6 +94,7 @@ public class SynStreamGenerateParseTest
Assert.assertEquals(associatedStreamId, synStream.getAssociatedStreamId());
Assert.assertEquals(flags, synStream.getFlags());
Assert.assertEquals(priority, synStream.getPriority());
+ Assert.assertEquals(slot, synStream.getSlot());
Assert.assertEquals(headers, synStream.getHeaders());
}
}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/TestSPDYParserListener.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/TestSPDYParserListener.java
index 4642ca5..14e0cf3 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/TestSPDYParserListener.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/TestSPDYParserListener.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/WindowUpdateGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/WindowUpdateGenerateParseTest.java
index c28b613..41bdf67 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/WindowUpdateGenerateParseTest.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/WindowUpdateGenerateParseTest.java
@@ -1,24 +1,21 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.frames;
import java.nio.ByteBuffer;
-import org.eclipse.jetty.io.StandardByteBufferPool;
+import org.eclipse.jetty.spdy.StandardByteBufferPool;
import org.eclipse.jetty.spdy.StandardCompressionFactory;
import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.generator.Generator;
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/LiveChromiumRequestParserTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/LiveChromiumRequestParserTest.java
index 56b65f0..46b7897 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/LiveChromiumRequestParserTest.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/LiveChromiumRequestParserTest.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/ParseVersusCacheBenchmarkTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/ParseVersusCacheBenchmarkTest.java
index abaac53..cc92e0c 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/ParseVersusCacheBenchmarkTest.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/ParseVersusCacheBenchmarkTest.java
@@ -1,18 +1,15 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/UnknownControlFrameTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/UnknownControlFrameTest.java
index 63171c6..12c470c 100644
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/UnknownControlFrameTest.java
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/UnknownControlFrameTest.java
@@ -1,11 +1,24 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.parser;
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import org.eclipse.jetty.io.StandardByteBufferPool;
import org.eclipse.jetty.spdy.SessionException;
+import org.eclipse.jetty.spdy.StandardByteBufferPool;
import org.eclipse.jetty.spdy.StandardCompressionFactory;
import org.eclipse.jetty.spdy.StreamException;
import org.eclipse.jetty.spdy.api.Headers;
@@ -23,7 +36,7 @@ public class UnknownControlFrameTest
@Test
public void testUnknownControlFrame() throws Exception
{
- SynStreamFrame frame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)0, new Headers());
+ SynStreamFrame frame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)0, (short)0, new Headers());
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
ByteBuffer buffer = generator.control(frame);
// Change the frame type to unknown
diff --git a/jetty-spdy/spdy-core/src/test/resources/keystore.jks b/jetty-spdy/spdy-core/src/test/resources/keystore.jks
new file mode 100644
index 0000000..428ba54
--- /dev/null
+++ b/jetty-spdy/spdy-core/src/test/resources/keystore.jks
Binary files differ
diff --git a/jetty-spdy/spdy-core/src/test/resources/truststore.jks b/jetty-spdy/spdy-core/src/test/resources/truststore.jks
new file mode 100644
index 0000000..839cb8c
--- /dev/null
+++ b/jetty-spdy/spdy-core/src/test/resources/truststore.jks
Binary files differ
diff --git a/jetty-spdy/spdy-jetty-http-webapp/pom.xml b/jetty-spdy/spdy-jetty-http-webapp/pom.xml
index 14ebb06..491f131 100644
--- a/jetty-spdy/spdy-jetty-http-webapp/pom.xml
+++ b/jetty-spdy/spdy-jetty-http-webapp/pom.xml
@@ -3,9 +3,8 @@
<parent>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-parent</artifactId>
- <version>9.0.0-SNAPSHOT</version>
+ <version>8.1.6-SNAPSHOT</version>
</parent>
-
<modelVersion>4.0.0</modelVersion>
<artifactId>spdy-jetty-http-webapp</artifactId>
<packaging>war</packaging>
@@ -40,7 +39,7 @@
<stopKey>quit</stopKey>
<jvmArgs>
-Dlog4j.configuration=file://${basedir}/src/main/resources/log4j.properties
- -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${project.version}/npn-boot-${project.version}.jar
+ -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn.version}/npn-boot-${npn.version}.jar
</jvmArgs>
<jettyXml>${basedir}/src/main/config/etc/jetty-spdy.xml</jettyXml>
<contextPath>/</contextPath>
@@ -61,4 +60,45 @@
-->
</plugins>
</build>
+
+<!--
+ <profiles>
+ <profile>
+ <id>proxy</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jetty-maven-plugin</artifactId>
+ <version>${project.version}</version>
+ <configuration>
+ <stopPort>8888</stopPort>
+ <stopKey>quit</stopKey>
+ <jvmArgs>
+ -Dlog4j.configuration=file://${basedir}/src/main/resources/log4j.properties
+ -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn.version}/npn-boot-${npn.version}.jar
+ -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005
+ </jvmArgs>
+ <jettyXml>${basedir}/src/main/config/etc/jetty-spdy-proxy.xml</jettyXml>
+ <contextPath>/</contextPath>
+ </configuration>
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty.spdy</groupId>
+ <artifactId>spdy-jetty-http</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>${slf4j-version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+-->
+
</project>
diff --git a/jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy-proxy.xml b/jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy-proxy.xml
new file mode 100644
index 0000000..9c637ec
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy-proxy.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+ <New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
+ <Set name="keyStorePath">src/main/resources/keystore.jks</Set>
+ <Set name="keyStorePassword">storepwd</Set>
+ <Set name="trustStore">src/main/resources/truststore.jks</Set>
+ <Set name="trustStorePassword">storepwd</Set>
+ <Set name="protocol">TLSv1</Set>
+ </New>
+
+ <!--
+ <Set class="org.eclipse.jetty.npn.NextProtoNego" name="debug" type="boolean">true</Set>
+ -->
+
+ <!--
+ This is the upstream server connector. It speaks non-SSL SPDY/2(HTTP) on port 9090.
+ -->
+ <Call name="addConnector">
+ <Arg>
+ <New class="org.eclipse.jetty.spdy.http.HTTPSPDYServerConnector">
+ <Set name="Port">9090</Set>
+ <Set name="defaultAsyncConnectionFactory">
+ <Call name="getAsyncConnectionFactory">
+ <Arg>spdy/2</Arg>
+ </Call>
+ </Set>
+ </New>
+ </Arg>
+ </Call>
+
+ <!--
+ This ProxyEngine translates the incoming SPDY/x(HTTP) request to SPDY/2(HTTP)
+ -->
+ <New id="spdyProxyEngine" class="org.eclipse.jetty.spdy.proxy.SPDYProxyEngine">
+ <Arg>
+ <New class="org.eclipse.jetty.spdy.SPDYClient$Factory">
+ <Call name="start"/>
+ </New>
+ </Arg>
+ </New>
+
+ <!--
+ The ProxyEngineSelector receives SPDY/x(HTTP) requests from proxy connectors below
+ and is configured to process requests for host "localhost".
+ Such requests are converted from SPDY/x(HTTP) to SPDY/2(HTTP) by the configured ProxyEngine
+ and forwarded to 127.0.0.1:9090, where they are served by the upstream server above.
+ -->
+ <New id="proxyEngineSelector" class="org.eclipse.jetty.spdy.proxy.ProxyEngineSelector">
+ <Call name="putProxyEngine">
+ <Arg>spdy/2</Arg>
+ <Arg><Ref id="spdyProxyEngine" /></Arg>
+ </Call>
+ <Set name="proxyServerInfos">
+ <Map>
+ <Entry>
+ <Item>localhost</Item>
+ <Item>
+ <New class="org.eclipse.jetty.spdy.proxy.ProxyEngineSelector$ProxyServerInfo">
+ <Arg type="String">spdy/2</Arg>
+ <Arg>127.0.0.1</Arg>
+ <Arg type="int">9090</Arg>
+ </New>
+ </Item>
+ </Entry>
+ </Map>
+ </Set>
+ </New>
+
+ <!--
+ These are the reverse proxy connectors accepting requests from clients.
+ They accept non-SSL (on port 8080) and SSL (on port 8443) HTTP,
+ SPDY/2(HTTP) and SPDY/3(HTTP).
+ Non-SPDY HTTP requests are converted to SPDY internally and passed to the
+ ProxyEngine above.
+ -->
+ <Call name="addConnector">
+ <Arg>
+ <New class="org.eclipse.jetty.spdy.proxy.HTTPSPDYProxyConnector">
+ <Arg><Ref id="proxyEngineSelector" /></Arg>
+ <Set name="Port">8080</Set>
+ </New>
+ </Arg>
+ </Call>
+ <Call name="addConnector">
+ <Arg>
+ <New class="org.eclipse.jetty.spdy.proxy.HTTPSPDYProxyConnector">
+ <Arg><Ref id="proxyEngineSelector" /></Arg>
+ <Arg><Ref id="sslContextFactory" /></Arg>
+ <Set name="Port">8443</Set>
+ </New>
+ </Arg>
+ </Call>
+
+
+</Configure>
diff --git a/jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy.xml b/jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy.xml
index 95c9c2b..0d847bc 100644
--- a/jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy.xml
+++ b/jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy.xml
@@ -11,9 +11,45 @@
<Set name="protocol">TLSv1</Set>
</New>
+ <!-- Uncomment to create a ReferrerPushStrategy that can be added to the Connectors -->
+
+ <!--
+ <New id="pushStrategy" class="org.eclipse.jetty.spdy.http.ReferrerPushStrategy">
+ <Arg type="List">
+ <Array type="String">
+ <Item>.*\.css</Item>
+ <Item>.*\.js</Item>
+ <Item>.*\.png</Item>
+ <Item>.*\.jpg</Item>
+ <Item>.*\.gif</Item>
+ </Array>
+ </Arg>
+ </New>
+ -->
+
+ <!--<Set class="org.eclipse.jetty.npn.NextProtoNego" name="debug" type="boolean">true</Set>-->
+
<Call name="addConnector">
<Arg>
<New class="org.eclipse.jetty.spdy.http.HTTPSPDYServerConnector">
+ <!-- uncomment to enable to apply ReferrerPushStrategy for spdy/3
+ if you want to support it in both spdy/2 and spdy/3, just replace the
+ value in the first map entry.
+ -->
+ <!--
+ <Arg name="pushStrategies">
+ <Map>
+ <Entry>
+ <Item type="short">2</Item>
+ <Item><New class="org.eclipse.jetty.spdy.http.PushStrategy$None" /></Item>
+ </Entry>
+ <Entry>
+ <Item type="short">3</Item>
+ <Item><Ref id="pushStrategy" /></Item>
+ </Entry>
+ </Map>
+ </Arg>
+ -->
<Set name="Port">8080</Set>
</New>
</Arg>
@@ -24,6 +60,24 @@
<Arg>
<Ref id="sslContextFactory" />
</Arg>
+ <!-- uncomment to enable to apply ReferrerPushStrategy for spdy/3
+ if you want to support it in both spdy/2 and spdy/3, just replace the
+ value in the first map entry.
+ -->
+ <!--
+ <Arg name="pushStrategies">
+ <Map>
+ <Entry>
+ <Item type="short">2</Item>
+ <Item><New class="org.eclipse.jetty.spdy.http.PushStrategy$None" /></Item>
+ </Entry>
+ <Entry>
+ <Item type="short">3</Item>
+ <Item><Ref id="pushStrategy" /></Item>
+ </Entry>
+ </Map>
+ </Arg>
+ -->
<Set name="Port">8443</Set>
</New>
</Arg>
diff --git a/jetty-spdy/spdy-jetty-http/pom.xml b/jetty-spdy/spdy-jetty-http/pom.xml
index c1a1165..948d342 100644
--- a/jetty-spdy/spdy-jetty-http/pom.xml
+++ b/jetty-spdy/spdy-jetty-http/pom.xml
@@ -3,9 +3,8 @@
<parent>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-parent</artifactId>
- <version>9.0.0-SNAPSHOT</version>
+ <version>8.1.6-SNAPSHOT</version>
</parent>
-
<modelVersion>4.0.0</modelVersion>
<artifactId>spdy-jetty-http</artifactId>
<name>Jetty :: SPDY :: Jetty HTTP Layer</name>
@@ -62,11 +61,22 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-client</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/AbstractHTTPSPDYServerConnector.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/AbstractHTTPSPDYServerConnector.java
new file mode 100644
index 0000000..543f783
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/AbstractHTTPSPDYServerConnector.java
@@ -0,0 +1,61 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.http;
+
+import java.io.IOException;
+
+import org.eclipse.jetty.http.HttpSchemes;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.spdy.SPDYServerConnector;
+import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+public class AbstractHTTPSPDYServerConnector extends SPDYServerConnector
+{
+ public AbstractHTTPSPDYServerConnector(ServerSessionFrameListener listener, SslContextFactory sslContextFactory)
+ {
+ super(listener, sslContextFactory);
+ }
+
+ @Override
+ public void customize(EndPoint endPoint, Request request) throws IOException
+ {
+ super.customize(endPoint, request);
+ if (getSslContextFactory() != null)
+ request.setScheme(HttpSchemes.HTTPS);
+ }
+
+ @Override
+ public boolean isConfidential(Request request)
+ {
+ if (getSslContextFactory() != null)
+ {
+ int confidentialPort = getConfidentialPort();
+ return confidentialPort == 0 || confidentialPort == request.getServerPort();
+ }
+ return super.isConfidential(request);
+ }
+
+ @Override
+ public boolean isIntegral(Request request)
+ {
+ if (getSslContextFactory() != null)
+ {
+ int integralPort = getIntegralPort();
+ return integralPort == 0 || integralPort == request.getServerPort();
+ }
+ return super.isIntegral(request);
+ }
+}
diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYHeader.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYHeader.java
new file mode 100644
index 0000000..7149aa5
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYHeader.java
@@ -0,0 +1,73 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.http;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jetty.spdy.api.SPDY;
+
+public enum HTTPSPDYHeader
+{
+ METHOD("method", ":method"),
+ URI("url", ":path"),
+ VERSION("version", ":version"),
+ SCHEME("scheme", ":scheme"),
+ HOST("host", ":host"),
+ STATUS("status", ":status");
+
+ public static HTTPSPDYHeader from(short version, String name)
+ {
+ switch (version)
+ {
+ case SPDY.V2:
+ return Names.v2Names.get(name);
+ case SPDY.V3:
+ return Names.v3Names.get(name);
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ private final String v2Name;
+ private final String v3Name;
+
+ private HTTPSPDYHeader(String v2Name, String v3Name)
+ {
+ this.v2Name = v2Name;
+ Names.v2Names.put(v2Name, this);
+ this.v3Name = v3Name;
+ Names.v3Names.put(v3Name, this);
+ }
+
+ public String name(short version)
+ {
+ switch (version)
+ {
+ case SPDY.V2:
+ return v2Name;
+ case SPDY.V3:
+ return v3Name;
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ private static class Names
+ {
+ private static final Map<String, HTTPSPDYHeader> v2Names = new HashMap<>();
+ private static final Map<String, HTTPSPDYHeader> v3Names = new HashMap<>();
+ }
+}
diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYServerConnector.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYServerConnector.java
index 63544a0..8b76589 100644
--- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYServerConnector.java
+++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYServerConnector.java
@@ -1,91 +1,64 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.http;
-import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
-import org.eclipse.jetty.http.HttpSchemes;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.spdy.AsyncConnectionFactory;
-import org.eclipse.jetty.spdy.SPDYServerConnector;
import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.util.ssl.SslContextFactory;
-public class HTTPSPDYServerConnector extends SPDYServerConnector
+public class HTTPSPDYServerConnector extends AbstractHTTPSPDYServerConnector
{
- private final AsyncConnectionFactory defaultConnectionFactory;
- private final PushStrategy pushStrategy = new PushStrategy.None();
-
public HTTPSPDYServerConnector()
{
- this(null);
+ this(null, Collections.<Short, PushStrategy>emptyMap());
}
- public HTTPSPDYServerConnector(SslContextFactory sslContextFactory)
+ public HTTPSPDYServerConnector(Map<Short, PushStrategy> pushStrategies)
{
- super(null, sslContextFactory);
- // Override the default connection factory for non-SSL connections
- defaultConnectionFactory = new ServerHTTPAsyncConnectionFactory(this);
+ this(null, pushStrategies);
}
- @Override
- protected void doStart() throws Exception
- {
- super.doStart();
- // Override the "spdy/2" protocol by handling HTTP over SPDY
- putAsyncConnectionFactory("spdy/2", new ServerHTTPSPDYAsyncConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), this, pushStrategy));
- // Add the "http/1.1" protocol for browsers that do not support NPN
- putAsyncConnectionFactory("http/1.1", new ServerHTTPAsyncConnectionFactory(this));
- }
-
- @Override
- protected AsyncConnectionFactory getDefaultAsyncConnectionFactory()
+ public HTTPSPDYServerConnector(SslContextFactory sslContextFactory)
{
- return defaultConnectionFactory;
+ this(sslContextFactory, Collections.<Short, PushStrategy>emptyMap());
}
- @Override
- public void customize(EndPoint endPoint, Request request) throws IOException
+ public HTTPSPDYServerConnector(SslContextFactory sslContextFactory, Map<Short, PushStrategy> pushStrategies)
{
- super.customize(endPoint, request);
- if (getSslContextFactory() != null)
- request.setScheme(HttpSchemes.HTTPS);
+ // We pass a null ServerSessionFrameListener because for
+ // HTTP over SPDY we need one that references the endPoint
+ super(null, sslContextFactory);
+ clearAsyncConnectionFactories();
+ // The "spdy/3" protocol handles HTTP over SPDY
+ putAsyncConnectionFactory("spdy/3", new ServerHTTPSPDYAsyncConnectionFactory(SPDY.V3, getByteBufferPool(), getExecutor(), getScheduler(), this, getPushStrategy(SPDY.V3,pushStrategies)));
+ // The "spdy/2" protocol handles HTTP over SPDY
+ putAsyncConnectionFactory("spdy/2", new ServerHTTPSPDYAsyncConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), this, getPushStrategy(SPDY.V2,pushStrategies)));
+ // The "http/1.1" protocol handles browsers that support NPN but not SPDY
+ putAsyncConnectionFactory("http/1.1", new ServerHTTPAsyncConnectionFactory(this));
+ // The default connection factory handles plain HTTP on non-SSL or non-NPN connections
+ setDefaultAsyncConnectionFactory(getAsyncConnectionFactory("http/1.1"));
}
- @Override
- public boolean isConfidential(Request request)
+ private PushStrategy getPushStrategy(short version, Map<Short, PushStrategy> pushStrategies)
{
- if (getSslContextFactory() != null)
- {
- int confidentialPort = getConfidentialPort();
- return confidentialPort == 0 || confidentialPort == request.getServerPort();
- }
- return super.isConfidential(request);
+ PushStrategy pushStrategy = pushStrategies.get(version);
+ if(pushStrategy == null)
+ pushStrategy = new PushStrategy.None();
+ return pushStrategy;
}
- @Override
- public boolean isIntegral(Request request)
- {
- if (getSslContextFactory() != null)
- {
- int integralPort = getIntegralPort();
- return integralPort == 0 || integralPort == request.getServerPort();
- }
- return super.isIntegral(request);
- }
}
diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/PushStrategy.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/PushStrategy.java
index 780a5b2..c15fe5b 100644
--- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/PushStrategy.java
+++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/PushStrategy.java
@@ -1,18 +1,16 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.http;
diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategy.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategy.java
index cf3fe06..0079897 100644
--- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategy.java
+++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategy.java
@@ -1,28 +1,28 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.http;
import java.util.Arrays;
import java.util.Collections;
-import java.util.LinkedHashSet;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Pattern;
import org.eclipse.jetty.spdy.api.Headers;
@@ -37,64 +37,99 @@ import org.eclipse.jetty.util.log.Logger;
* will have a <tt>Referer</tt> HTTP header that points to <tt>index.html</tt>, which we
* use to link the associated resource to the main resource.</p>
* <p>However, also following a hyperlink generates a HTTP request with a <tt>Referer</tt>
- * HTTP header that points to <tt>index.html</tt>; therefore main resources and associated
- * resources must be distinguishable.</p>
- * <p>This class distinguishes associated resources by their URL path suffix.
+ * HTTP header that points to <tt>index.html</tt>; therefore a proper value for {@link #getReferrerPushPeriod()}
+ * has to be set. If the referrerPushPeriod for a main resource has been passed, no more
+ * associated resources will be added for that main resource.</p>
+ * <p>This class distinguishes associated main resources by their URL path suffix and content
+ * type.
* CSS stylesheets, images and JavaScript files have recognizable URL path suffixes that
- * are classified as associated resources.</p>
- * <p>Note however, that CSS stylesheets may refer to images, and the CSS image request
- * will have the CSS stylesheet as referrer, so there is some degree of recursion that
- * needs to be handled.</p>
- *
- * TODO: this class is kind-of leaking since the resources map is always adding entries
- * TODO: although these entries will be limited by the number of application pages.
- * TODO: however, there is no ConcurrentLinkedHashMap yet in JDK (there is in Guava though)
- * TODO: so we cannot use the built-in LRU features of LinkedHashMap
- *
- * TODO: Wikipedia maps URLs like http://en.wikipedia.org/wiki/File:PNG-Gradient_hex.png
- * TODO: to text/html, so perhaps we need to improve isPushResource() by looking at the
- * TODO: response Content-Type header, and not only at the URL extension
+ * are classified as associated resources. The suffix regexs can be configured by constructor argument</p>
+ * <p>When CSS stylesheets refer to images, the CSS image request will have the CSS
+ * stylesheet as referrer. This implementation will push also the CSS image.</p>
+ * <p>The push metadata built by this implementation is limited by the number of pages
+ * of the application itself, and by the
+ * {@link #getMaxAssociatedResources() max associated resources} parameter.
+ * This parameter limits the number of associated resources per each main resource, so
+ * that if a main resource has hundreds of associated resources, only up to the number
+ * specified by this parameter will be pushed.</p>
*/
public class ReferrerPushStrategy implements PushStrategy
{
private static final Logger logger = Log.getLogger(ReferrerPushStrategy.class);
- private final ConcurrentMap<String, Set<String>> resources = new ConcurrentHashMap<>();
- private final Set<Pattern> pushRegexps = new LinkedHashSet<>();
- private final Set<Pattern> allowedPushOrigins = new LinkedHashSet<>();
+ private final ConcurrentMap<String, MainResource> mainResources = new ConcurrentHashMap<>();
+ private final Set<Pattern> pushRegexps = new HashSet<>();
+ private final Set<String> pushContentTypes = new HashSet<>();
+ private final Set<Pattern> allowedPushOrigins = new HashSet<>();
+ private volatile int maxAssociatedResources = 32;
+ private volatile int referrerPushPeriod = 5000;
public ReferrerPushStrategy()
{
- this(Arrays.asList(".*\\.css", ".*\\.js", ".*\\.png", ".*\\.jpg", ".*\\.gif"));
+ this(Arrays.asList(".*\\.css", ".*\\.js", ".*\\.png", ".*\\.jpeg", ".*\\.jpg", ".*\\.gif", ".*\\.ico"));
}
public ReferrerPushStrategy(List<String> pushRegexps)
{
- this(pushRegexps, Collections.<String>emptyList());
+ this(pushRegexps, Arrays.asList(
+ "text/css",
+ "text/javascript", "application/javascript", "application/x-javascript",
+ "image/png", "image/x-png",
+ "image/jpeg",
+ "image/gif",
+ "image/x-icon", "image/vnd.microsoft.icon"));
+ }
+
+ public ReferrerPushStrategy(List<String> pushRegexps, List<String> pushContentTypes)
+ {
+ this(pushRegexps, pushContentTypes, Collections.<String>emptyList());
}
- public ReferrerPushStrategy(List<String> pushRegexps, List<String> allowedPushOrigins)
+ public ReferrerPushStrategy(List<String> pushRegexps, List<String> pushContentTypes, List<String> allowedPushOrigins)
{
for (String pushRegexp : pushRegexps)
this.pushRegexps.add(Pattern.compile(pushRegexp));
+ this.pushContentTypes.addAll(pushContentTypes);
for (String allowedPushOrigin : allowedPushOrigins)
this.allowedPushOrigins.add(Pattern.compile(allowedPushOrigin.replace(".", "\\.").replace("*", ".*")));
}
+ public int getMaxAssociatedResources()
+ {
+ return maxAssociatedResources;
+ }
+
+ public void setMaxAssociatedResources(int maxAssociatedResources)
+ {
+ this.maxAssociatedResources = maxAssociatedResources;
+ }
+
+ public int getReferrerPushPeriod()
+ {
+ return referrerPushPeriod;
+ }
+
+ public void setReferrerPushPeriod(int referrerPushPeriod)
+ {
+ this.referrerPushPeriod = referrerPushPeriod;
+ }
+
@Override
public Set<String> apply(Stream stream, Headers requestHeaders, Headers responseHeaders)
{
- Set<String> result = Collections.emptySet();
- String scheme = requestHeaders.get("scheme").value();
- String host = requestHeaders.get("host").value();
- String origin = new StringBuilder(scheme).append("://").append(host).toString();
- String url = requestHeaders.get("url").value();
- String absoluteURL = new StringBuilder(origin).append(url).toString();
- logger.debug("Applying push strategy for {}", absoluteURL);
- if (isValidMethod(requestHeaders.get("method").value()))
+ Set<String> result = Collections.<String>emptySet();
+ short version = stream.getSession().getVersion();
+ if (!isIfModifiedSinceHeaderPresent(requestHeaders) && isValidMethod(requestHeaders.get(HTTPSPDYHeader.METHOD.name(version)).value()))
{
+ String scheme = requestHeaders.get(HTTPSPDYHeader.SCHEME.name(version)).value();
+ String host = requestHeaders.get(HTTPSPDYHeader.HOST.name(version)).value();
+ String origin = scheme + "://" + host;
+ String url = requestHeaders.get(HTTPSPDYHeader.URI.name(version)).value();
+ String absoluteURL = origin + url;
+ logger.debug("Applying push strategy for {}", absoluteURL);
if (isMainResource(url, responseHeaders))
{
- result = pushResources(absoluteURL);
+ MainResource mainResource = getOrCreateMainResource(absoluteURL);
+ result = mainResource.getResources();
}
else if (isPushResource(url, responseHeaders))
{
@@ -102,18 +137,49 @@ public class ReferrerPushStrategy implements PushStrategy
if (referrerHeader != null)
{
String referrer = referrerHeader.value();
- Set<String> pushResources = resources.get(referrer);
- if (pushResources == null || !pushResources.contains(url))
- buildMetadata(origin, url, referrer);
+ MainResource mainResource = mainResources.get(referrer);
+ if (mainResource == null)
+ mainResource = getOrCreateMainResource(referrer);
+
+ Set<String> pushResources = mainResource.getResources();
+ if (!pushResources.contains(url))
+ mainResource.addResource(url, origin, referrer);
else
- result = pushResources(absoluteURL);
+ result = getPushResources(absoluteURL);
}
}
+ logger.debug("Pushing {} resources for {}: {}", result.size(), absoluteURL, result);
}
- logger.debug("Push resources for {}: {}", absoluteURL, result);
return result;
}
+ private Set<String> getPushResources(String absoluteURL)
+ {
+ Set<String> result = Collections.emptySet();
+ if (mainResources.get(absoluteURL) != null)
+ result = mainResources.get(absoluteURL).getResources();
+ return result;
+ }
+
+ private MainResource getOrCreateMainResource(String absoluteURL)
+ {
+ MainResource mainResource = mainResources.get(absoluteURL);
+ if (mainResource == null)
+ {
+ logger.debug("Creating new main resource for {}", absoluteURL);
+ MainResource value = new MainResource(absoluteURL);
+ mainResource = mainResources.putIfAbsent(absoluteURL, value);
+ if (mainResource == null)
+ mainResource = value;
+ }
+ return mainResource;
+ }
+
+ private boolean isIfModifiedSinceHeaderPresent(Headers headers)
+ {
+ return headers.get("if-modified-since") != null;
+ }
+
private boolean isValidMethod(String method)
{
return "GET".equalsIgnoreCase(method);
@@ -129,43 +195,85 @@ public class ReferrerPushStrategy implements PushStrategy
for (Pattern pushRegexp : pushRegexps)
{
if (pushRegexp.matcher(url).matches())
- return true;
+ {
+ Headers.Header header = responseHeaders.get("content-type");
+ if (header == null)
+ return true;
+
+ String contentType = header.value().toLowerCase();
+ for (String pushContentType : pushContentTypes)
+ if (contentType.startsWith(pushContentType))
+ return true;
+ }
}
return false;
}
- private Set<String> pushResources(String absoluteURL)
+ private class MainResource
{
- Set<String> pushResources = resources.get(absoluteURL);
- if (pushResources == null)
- return Collections.emptySet();
- return Collections.unmodifiableSet(pushResources);
- }
+ private final String name;
+ private final Set<String> resources = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
+ private final AtomicLong firstResourceAdded = new AtomicLong(-1);
- private void buildMetadata(String origin, String url, String referrer)
- {
- if (referrer.startsWith(origin) || isPushOriginAllowed(origin))
+ private MainResource(String name)
{
- Set<String> pushResources = resources.get(referrer);
- if (pushResources == null)
+ this.name = name;
+ }
+
+ public boolean addResource(String url, String origin, String referrer)
+ {
+ // We start the push period here and not when initializing the main resource, because a browser with a
+ // prefilled cache won't request the subresources. If the browser with warmed up cache now hits the main
+ // resource after a server restart, the push period shouldn't start until the first subresource is
+ // being requested.
+ firstResourceAdded.compareAndSet(-1, System.nanoTime());
+
+ long delay = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - firstResourceAdded.get());
+ if (!referrer.startsWith(origin) && !isPushOriginAllowed(origin))
+ {
+ logger.debug("Skipped store of push metadata {} for {}: Origin: {} doesn't match or origin not allowed",
+ url, name, origin);
+ return false;
+ }
+
+ // This check is not strictly concurrent-safe, but limiting
+ // the number of associated resources is achieved anyway
+ // although in rare cases few more resources will be stored
+ if (resources.size() >= maxAssociatedResources)
{
- pushResources = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
- Set<String> existing = resources.putIfAbsent(referrer, pushResources);
- if (existing != null)
- pushResources = existing;
+ logger.debug("Skipped store of push metadata {} for {}: max associated resources ({}) reached",
+ url, name, maxAssociatedResources);
+ return false;
}
- pushResources.add(url);
- logger.debug("Built push metadata for {}: {}", referrer, pushResources);
+ if (delay > referrerPushPeriod)
+ {
+ logger.debug("Delay: {}ms longer than referrerPushPeriod: {}ms. Not adding resource: {} for: {}", delay, referrerPushPeriod, url, name);
+ return false;
+ }
+
+ logger.debug("Adding resource: {} for: {} with delay: {}ms.", url, name, delay);
+ resources.add(url);
+ return true;
}
- }
- private boolean isPushOriginAllowed(String origin)
- {
- for (Pattern allowedPushOrigin : allowedPushOrigins)
+ public Set<String> getResources()
{
- if (allowedPushOrigin.matcher(origin).matches())
- return true;
+ return Collections.unmodifiableSet(resources);
+ }
+
+ public String toString()
+ {
+ return "MainResource: " + name + " associated resources:" + resources.size();
+ }
+
+ private boolean isPushOriginAllowed(String origin)
+ {
+ for (Pattern allowedPushOrigin : allowedPushOrigins)
+ {
+ if (allowedPushOrigin.matcher(origin).matches())
+ return true;
+ }
+ return false;
}
- return false;
}
}
diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPAsyncConnectionFactory.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPAsyncConnectionFactory.java
index 3622b2f..b0bae06 100644
--- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPAsyncConnectionFactory.java
+++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPAsyncConnectionFactory.java
@@ -1,18 +1,16 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.http;
@@ -21,18 +19,23 @@ import java.nio.channels.SocketChannel;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.nio.AsyncConnection;
import org.eclipse.jetty.server.AsyncHttpConnection;
-import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.spdy.AsyncConnectionFactory;
+import org.eclipse.jetty.spdy.SPDYServerConnector;
public class ServerHTTPAsyncConnectionFactory implements AsyncConnectionFactory
{
- private final Connector connector;
+ private final SPDYServerConnector connector;
- public ServerHTTPAsyncConnectionFactory(Connector connector)
+ public ServerHTTPAsyncConnectionFactory(SPDYServerConnector connector)
{
this.connector = connector;
}
+ public SPDYServerConnector getConnector()
+ {
+ return connector;
+ }
+
@Override
public AsyncConnection newAsyncConnection(SocketChannel channel, AsyncEndPoint endPoint, Object attachment)
{
diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnection.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnection.java
index da14dbe..ecc241e 100644
--- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnection.java
+++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnection.java
@@ -1,18 +1,16 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.http;
@@ -24,8 +22,10 @@ import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import org.eclipse.jetty.http.HttpException;
import org.eclipse.jetty.http.HttpFields;
@@ -47,14 +47,16 @@ import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.spdy.SPDYAsyncConnection;
import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
+import org.eclipse.jetty.spdy.api.BytesDataInfo;
import org.eclipse.jetty.spdy.api.DataInfo;
+import org.eclipse.jetty.spdy.api.Handler;
import org.eclipse.jetty.spdy.api.Headers;
import org.eclipse.jetty.spdy.api.ReplyInfo;
import org.eclipse.jetty.spdy.api.RstInfo;
+import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.api.StreamStatus;
import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -66,6 +68,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
private final Queue<Runnable> tasks = new LinkedList<>();
private final BlockingQueue<DataInfo> dataInfos = new LinkedBlockingQueue<>();
+ private final short version;
private final SPDYAsyncConnection connection;
private final PushStrategy pushStrategy;
private final Stream stream;
@@ -75,9 +78,10 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
private volatile State state = State.INITIAL;
private boolean dispatched; // Guarded by synchronization on tasks
- public ServerHTTPSPDYAsyncConnection(Connector connector, AsyncEndPoint endPoint, Server server, SPDYAsyncConnection connection, PushStrategy pushStrategy, Stream stream)
+ public ServerHTTPSPDYAsyncConnection(Connector connector, AsyncEndPoint endPoint, Server server, short version, SPDYAsyncConnection connection, PushStrategy pushStrategy, Stream stream)
{
super(connector, endPoint, server);
+ this.version = version;
this.connection = connection;
this.pushStrategy = pushStrategy;
this.stream = stream;
@@ -159,14 +163,12 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
}
case REQUEST:
{
- Headers.Header method = headers.get("method");
- Headers.Header uri = headers.get("url");
- Headers.Header version = headers.get("version");
+ Headers.Header method = headers.get(HTTPSPDYHeader.METHOD.name(version));
+ Headers.Header uri = headers.get(HTTPSPDYHeader.URI.name(version));
+ Headers.Header version = headers.get(HTTPSPDYHeader.VERSION.name(this.version));
if (method == null || uri == null || version == null)
- {
throw new HttpException(HttpStatus.BAD_REQUEST_400);
- }
String m = method.value();
String u = uri.value();
@@ -174,6 +176,10 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
logger.debug("HTTP > {} {} {}", m, u, v);
startRequest(new ByteArrayBuffer(m), new ByteArrayBuffer(u), new ByteArrayBuffer(v));
+ Headers.Header schemeHeader = headers.get(HTTPSPDYHeader.SCHEME.name(this.version));
+ if(schemeHeader != null)
+ _request.setScheme(schemeHeader.value());
+
updateState(State.HEADERS);
handle();
break;
@@ -183,15 +189,19 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
for (Headers.Header header : headers)
{
String name = header.name();
- switch (name)
+
+ // Skip special SPDY headers, unless it's the "host" header
+ HTTPSPDYHeader specialHeader = HTTPSPDYHeader.from(version, name);
+ if (specialHeader != null)
{
- case "method":
- case "version":
- case "url":
- {
- // Skip request line headers
+ if (specialHeader == HTTPSPDYHeader.HOST)
+ name = "host";
+ else
continue;
- }
+ }
+
+ switch (name)
+ {
case "connection":
case "keep-alive":
case "proxy-connection":
@@ -266,8 +276,8 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
else
{
Headers headers = new Headers();
- headers.put("status", String.valueOf(status));
- headers.put("version", "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.STATUS.name(version), String.valueOf(status));
+ headers.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
stream.reply(new ReplyInfo(headers, true));
}
}
@@ -395,39 +405,67 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
{
if (!stream.isUnidirectional())
stream.reply(replyInfo);
- if (replyInfo.getHeaders().get("status").value().startsWith("200") && !stream.isClosed())
+ if (replyInfo.getHeaders().get(HTTPSPDYHeader.STATUS.name(version)).value().startsWith("200") &&
+ !stream.isClosed())
{
// We have a 200 OK with some content to send
- Headers.Header scheme = headers.get("scheme");
- Headers.Header host = headers.get("host");
- Headers.Header url = headers.get("url");
- Set<String> pushResources = pushStrategy.apply(stream, this.headers, replyInfo.getHeaders());
- String referrer = new StringBuilder(scheme.value()).append("://").append(host.value()).append(url.value()).toString();
- for (String pushURL : pushResources)
+ Headers.Header scheme = headers.get(HTTPSPDYHeader.SCHEME.name(version));
+ Headers.Header host = headers.get(HTTPSPDYHeader.HOST.name(version));
+ Headers.Header uri = headers.get(HTTPSPDYHeader.URI.name(version));
+ Set<String> pushResources = pushStrategy.apply(stream, headers, replyInfo.getHeaders());
+
+ for (String pushResourcePath : pushResources)
{
- final Headers pushHeaders = new Headers();
- pushHeaders.put("method", "GET");
- pushHeaders.put("url", pushURL);
- pushHeaders.put("version", "HTTP/1.1");
- pushHeaders.put(scheme);
- pushHeaders.put(host);
- pushHeaders.put("referer", referrer);
- // Remember support for gzip encoding
- pushHeaders.put(headers.get("accept-encoding"));
- stream.syn(new SynInfo(pushHeaders, false), getMaxIdleTime(), TimeUnit.MILLISECONDS, new Callback.Adapter<Stream>()
+ final Headers requestHeaders = createRequestHeaders(scheme, host, uri, pushResourcePath);
+ final Headers pushHeaders = createPushHeaders(scheme, host, pushResourcePath);
+
+ stream.syn(new SynInfo(pushHeaders, false), getMaxIdleTime(), TimeUnit.MILLISECONDS, new Handler.Adapter<Stream>()
{
@Override
public void completed(Stream pushStream)
{
- Synchronous pushConnection = new Synchronous(getHttpConnector(), getEndPoint(), getServer(), connection, pushStrategy, pushStream);
- pushConnection.beginRequest(pushHeaders, true);
+ ServerHTTPSPDYAsyncConnection pushConnection =
+ new ServerHTTPSPDYAsyncConnection(getConnector(), getEndPoint(), getServer(), version, connection, pushStrategy, pushStream);
+ pushConnection.beginRequest(requestHeaders, true);
}
});
}
}
}
+ private Headers createRequestHeaders(Headers.Header scheme, Headers.Header host, Headers.Header uri, String pushResourcePath)
+ {
+ final Headers requestHeaders = new Headers();
+ requestHeaders.put(HTTPSPDYHeader.METHOD.name(version), "GET");
+ requestHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
+ requestHeaders.put(scheme);
+ requestHeaders.put(host);
+ requestHeaders.put(HTTPSPDYHeader.URI.name(version), pushResourcePath);
+ String referrer = scheme.value() + "://" + host.value() + uri.value();
+ requestHeaders.put("referer", referrer);
+ // Remember support for gzip encoding
+ requestHeaders.put(headers.get("accept-encoding"));
+ requestHeaders.put("x-spdy-push", "true");
+ return requestHeaders;
+ }
+
+ private Headers createPushHeaders(Headers.Header scheme, Headers.Header host, String pushResourcePath)
+ {
+ final Headers pushHeaders = new Headers();
+ if (version == SPDY.V2)
+ pushHeaders.put(HTTPSPDYHeader.URI.name(version), scheme.value() + "://" + host.value() + pushResourcePath);
+ else
+ {
+ pushHeaders.put(HTTPSPDYHeader.URI.name(version), pushResourcePath);
+ pushHeaders.put(scheme);
+ pushHeaders.put(host);
+ }
+ pushHeaders.put(HTTPSPDYHeader.STATUS.name(version), "200");
+ pushHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
+ return pushHeaders;
+ }
+
private Buffer consumeContent(long maxIdleTime) throws IOException, InterruptedException
{
while (true)
@@ -609,11 +647,11 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
{
Headers headers = new Headers();
String version = "HTTP/1.1";
- headers.put("version", version);
+ headers.put(HTTPSPDYHeader.VERSION.name(ServerHTTPSPDYAsyncConnection.this.version), version);
StringBuilder status = new StringBuilder().append(_status);
if (_reason != null)
status.append(" ").append(_reason.toString("UTF-8"));
- headers.put("status", status.toString());
+ headers.put(HTTPSPDYHeader.STATUS.name(ServerHTTPSPDYAsyncConnection.this.version), status.toString());
logger.debug("HTTP < {} {}", version, status);
if (fields != null)
@@ -629,19 +667,14 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
}
// We have to query the HttpGenerator and its buffers to know
- // whether there is content buffered; if so, send the data frame
+ // whether there is content buffered and update the generator state
Buffer content = getContentBuffer();
reply(stream, new ReplyInfo(headers, content == null));
if (content != null)
{
- closed = allContentAdded || isAllContentWritten();
- ByteBuffer buffer = ByteBuffer.wrap(content.asArray());
- logger.debug("HTTP < {} bytes of content", buffer.remaining());
- // Send the data frame
- stream.data(new ByteBufferDataInfo(buffer, closed));
+ closed = false;
// Update HttpGenerator fields so that they remain consistent
- content.clear();
- _state = closed ? HttpGenerator.STATE_END : HttpGenerator.STATE_CONTENT;
+ _state = HttpGenerator.STATE_CONTENT;
}
else
{
@@ -655,7 +688,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
{
if (_buffer != null && _buffer.length() > 0)
return _buffer;
- if (_bypass && _content != null && _content.length() > 0)
+ if (_content != null && _content.length() > 0)
return _content;
return null;
}
@@ -680,22 +713,48 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
@Override
public void flush(long maxIdleTime) throws IOException
{
- while (_content != null && _content.length() > 0)
+ try
{
- _content.skip(_buffer.put(_content));
- ByteBuffer buffer = ByteBuffer.wrap(_buffer.asArray());
- logger.debug("HTTP < {} bytes of content", buffer.remaining());
- _buffer.clear();
- closed = _content.length() == 0 && _last;
- stream.data(new ByteBufferDataInfo(buffer, closed));
-
- boolean expired = !connection.getEndPoint().blockWritable(maxIdleTime);
- if (expired)
+ Buffer content = getContentBuffer();
+ while (content != null)
{
- stream.getSession().goAway();
- throw new EOFException("write timeout");
+ DataInfo dataInfo = toDataInfo(content, closed);
+ logger.debug("HTTP < {} bytes of content", dataInfo.length());
+ stream.data(dataInfo).get(maxIdleTime, TimeUnit.MILLISECONDS);
+ content.clear();
+ _bypass = false;
+ content = getContentBuffer();
}
}
+ catch (TimeoutException x)
+ {
+ stream.getSession().goAway();
+ throw new EOFException("write timeout");
+ }
+ catch (InterruptedException x)
+ {
+ throw new InterruptedIOException();
+ }
+ catch (ExecutionException x)
+ {
+ throw new IOException(x.getCause());
+ }
+ }
+
+ private DataInfo toDataInfo(Buffer buffer, boolean close)
+ {
+ if (buffer instanceof ByteArrayBuffer)
+ return new BytesDataInfo(buffer.array(), buffer.getIndex(), buffer.length(), close);
+
+ if (buffer instanceof NIOBuffer)
+ {
+ ByteBuffer byteBuffer = ((NIOBuffer)buffer).getByteBuffer();
+ byteBuffer.limit(buffer.putIndex());
+ byteBuffer.position(buffer.getIndex());
+ return new ByteBufferDataInfo(byteBuffer, close);
+ }
+
+ return new BytesDataInfo(buffer.asArray(), close);
}
@Override
@@ -722,35 +781,17 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
Buffer content = getContentBuffer();
if (content != null)
{
- ByteBuffer buffer = ByteBuffer.wrap(content.asArray());
- logger.debug("HTTP < {} bytes of content", buffer.remaining());
- // Update HttpGenerator fields so that they remain consistent
- content.clear();
+ closed = true;
_state = STATE_END;
- // Send the data frame
- stream.data(new ByteBufferDataInfo(buffer, true));
+ flush(getMaxIdleTime());
}
else if (!closed)
{
closed = true;
_state = STATE_END;
- // Send the data frame
+ // Send the last, empty, data frame
stream.data(new ByteBufferDataInfo(ZERO_BYTES, true));
}
}
}
-
- private static class Synchronous extends ServerHTTPSPDYAsyncConnection
- {
- private Synchronous(Connector connector, AsyncEndPoint endPoint, Server server, SPDYAsyncConnection connection, PushStrategy pushStrategy, Stream stream)
- {
- super(connector, endPoint, server, connection, pushStrategy, stream);
- }
-
- @Override
- protected void execute(Runnable task)
- {
- task.run();
- }
- }
}
diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnectionFactory.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnectionFactory.java
index 25b3d3b..7a5f381 100644
--- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnectionFactory.java
+++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnectionFactory.java
@@ -1,18 +1,16 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.http;
@@ -20,8 +18,8 @@ import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import org.eclipse.jetty.io.AsyncEndPoint;
-import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.spdy.ByteBufferPool;
import org.eclipse.jetty.spdy.EmptyAsyncEndPoint;
import org.eclipse.jetty.spdy.SPDYAsyncConnection;
import org.eclipse.jetty.spdy.ServerSPDYAsyncConnectionFactory;
@@ -52,7 +50,7 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
}
@Override
- protected ServerSessionFrameListener newServerSessionFrameListener(AsyncEndPoint endPoint, Object attachment)
+ protected ServerSessionFrameListener provideServerSessionFrameListener(AsyncEndPoint endPoint, Object attachment)
{
return new HTTPServerFrameListener(endPoint);
}
@@ -78,8 +76,8 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
logger.debug("Received {} on {}", synInfo, stream);
HTTPSPDYAsyncEndPoint asyncEndPoint = new HTTPSPDYAsyncEndPoint(endPoint, stream);
- ServerHTTPSPDYAsyncConnection connection = new ServerHTTPSPDYAsyncConnection(connector,
- asyncEndPoint, connector.getServer(), (SPDYAsyncConnection)endPoint.getHttpChannel(),
+ ServerHTTPSPDYAsyncConnection connection = new ServerHTTPSPDYAsyncConnection(connector, asyncEndPoint,
+ connector.getServer(), getVersion(), (SPDYAsyncConnection)endPoint.getConnection(),
pushStrategy, stream);
asyncEndPoint.setConnection(connection);
stream.setAttribute(CONNECTION_ATTRIBUTE, connection);
diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/HTTPSPDYProxyConnector.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/HTTPSPDYProxyConnector.java
new file mode 100644
index 0000000..2827af0
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/HTTPSPDYProxyConnector.java
@@ -0,0 +1,39 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.proxy;
+
+import org.eclipse.jetty.spdy.ServerSPDYAsyncConnectionFactory;
+import org.eclipse.jetty.spdy.api.SPDY;
+import org.eclipse.jetty.spdy.http.AbstractHTTPSPDYServerConnector;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+public class HTTPSPDYProxyConnector extends AbstractHTTPSPDYServerConnector
+{
+ public HTTPSPDYProxyConnector(ProxyEngineSelector proxyEngineSelector)
+ {
+ this(proxyEngineSelector, null);
+ }
+
+ public HTTPSPDYProxyConnector(ProxyEngineSelector proxyEngineSelector, SslContextFactory sslContextFactory)
+ {
+ super(proxyEngineSelector, sslContextFactory);
+ clearAsyncConnectionFactories();
+
+ putAsyncConnectionFactory("spdy/3", new ServerSPDYAsyncConnectionFactory(SPDY.V3, getByteBufferPool(), getExecutor(), getScheduler(), proxyEngineSelector));
+ putAsyncConnectionFactory("spdy/2", new ServerSPDYAsyncConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), proxyEngineSelector));
+ putAsyncConnectionFactory("http/1.1", new ProxyHTTPAsyncConnectionFactory(this, SPDY.V2, proxyEngineSelector));
+ setDefaultAsyncConnectionFactory(getAsyncConnectionFactory("http/1.1"));
+ }
+}
diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyEngine.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyEngine.java
new file mode 100644
index 0000000..0a17261
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyEngine.java
@@ -0,0 +1,94 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.proxy;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.eclipse.jetty.spdy.api.Headers;
+import org.eclipse.jetty.spdy.api.Stream;
+import org.eclipse.jetty.spdy.api.StreamFrameListener;
+import org.eclipse.jetty.spdy.api.SynInfo;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * <p>{@link ProxyEngine} is the class for SPDY proxy functionalities that receives a SPDY request and converts it to
+ * any protocol to its server side.</p>
+ * <p>This class listens for SPDY events sent by clients; subclasses are responsible for translating
+ * these SPDY client events into appropriate events to forward to the server, in the appropriate
+ * protocol that is understood by the server.</p>
+ */
+public abstract class ProxyEngine
+{
+ protected final Logger logger = Log.getLogger(getClass());
+ private final String name;
+
+ protected ProxyEngine()
+ {
+ this(name());
+ }
+
+ private static String name()
+ {
+ try
+ {
+ return InetAddress.getLocalHost().getHostName();
+ }
+ catch (UnknownHostException x)
+ {
+ return "localhost";
+ }
+ }
+
+ public abstract StreamFrameListener proxy(Stream clientStream, SynInfo clientSynInfo, ProxyEngineSelector.ProxyServerInfo proxyServerInfo);
+
+ protected ProxyEngine(String name)
+ {
+ this.name = name;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ protected void addRequestProxyHeaders(Stream stream, Headers headers)
+ {
+ addViaHeader(headers);
+ String address = (String)stream.getSession().getAttribute("org.eclipse.jetty.spdy.remoteAddress");
+ if (address != null)
+ headers.add("X-Forwarded-For", address);
+ }
+
+ protected void addResponseProxyHeaders(Stream stream, Headers headers)
+ {
+ addViaHeader(headers);
+ }
+
+ private void addViaHeader(Headers headers)
+ {
+ headers.add("Via", "http/1.1 " + getName());
+ }
+
+ protected void customizeRequestHeaders(Stream stream, Headers headers)
+ {
+ }
+
+ protected void customizeResponseHeaders(Stream stream, Headers headers)
+ {
+ }
+
+}
diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyEngineSelector.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyEngineSelector.java
new file mode 100644
index 0000000..52855be
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyEngineSelector.java
@@ -0,0 +1,169 @@
+package org.eclipse.jetty.spdy.proxy;
+
+import java.net.InetSocketAddress;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.jetty.spdy.api.GoAwayInfo;
+import org.eclipse.jetty.spdy.api.Headers;
+import org.eclipse.jetty.spdy.api.PingInfo;
+import org.eclipse.jetty.spdy.api.RstInfo;
+import org.eclipse.jetty.spdy.api.Session;
+import org.eclipse.jetty.spdy.api.Stream;
+import org.eclipse.jetty.spdy.api.StreamFrameListener;
+import org.eclipse.jetty.spdy.api.StreamStatus;
+import org.eclipse.jetty.spdy.api.SynInfo;
+import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
+import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * <p>{@link ProxyEngineSelector} is the main entry point for syn stream events of a jetty SPDY proxy. It receives the
+ * syn stream frames from the clients, checks if there's an appropriate {@link ProxyServerInfo} for the given target
+ * host and forwards the syn to a {@link ProxyEngine} for the protocol defined in {@link ProxyServerInfo}.</p>
+ *
+ * <p>If no {@link ProxyServerInfo} can be found for the given target host or no {@link ProxyEngine} can be found for
+ * the given protocol, it resets the client stream.</p>
+ *
+ * <p>This class also provides configuration for the proxy rules.</p>
+ */
+public class ProxyEngineSelector extends ServerSessionFrameListener.Adapter
+{
+ protected final Logger logger = Log.getLogger(getClass());
+ private final Map<String, ProxyServerInfo> proxyInfos = new ConcurrentHashMap<>();
+ private final Map<String, ProxyEngine> proxyEngines = new ConcurrentHashMap<>();
+
+ @Override
+ public final StreamFrameListener onSyn(final Stream clientStream, SynInfo clientSynInfo)
+ {
+ logger.debug("C -> P {} on {}", clientSynInfo, clientStream);
+
+ final Session clientSession = clientStream.getSession();
+ short clientVersion = clientSession.getVersion();
+ Headers headers = new Headers(clientSynInfo.getHeaders(), false);
+
+ Headers.Header hostHeader = headers.get(HTTPSPDYHeader.HOST.name(clientVersion));
+ if (hostHeader == null)
+ {
+ logger.debug("No host header found: " + headers);
+ rst(clientStream);
+ return null;
+ }
+
+ String host = hostHeader.value();
+ int colon = host.indexOf(':');
+ if (colon >= 0)
+ host = host.substring(0, colon);
+
+ ProxyServerInfo proxyServerInfo = getProxyServerInfo(host);
+ if (proxyServerInfo == null)
+ {
+ logger.debug("No matching ProxyServerInfo found for: " + host);
+ rst(clientStream);
+ return null;
+ }
+
+ String protocol = proxyServerInfo.getProtocol();
+ ProxyEngine proxyEngine = proxyEngines.get(protocol);
+ if (proxyEngine == null)
+ {
+ logger.debug("No matching ProxyEngine found for: " + protocol);
+ rst(clientStream);
+ return null;
+ }
+
+ return proxyEngine.proxy(clientStream, clientSynInfo, proxyServerInfo);
+ }
+
+ @Override
+ public void onPing(Session clientSession, PingInfo pingInfo)
+ {
+ // We do not know to which upstream server
+ // to send the PING so we just ignore it
+ }
+
+ @Override
+ public void onGoAway(Session session, GoAwayInfo goAwayInfo)
+ {
+ // TODO:
+ }
+
+ public Map<String, ProxyEngine> getProxyEngines()
+ {
+ return new HashMap<>(proxyEngines);
+ }
+
+ public void setProxyEngines(Map<String, ProxyEngine> proxyEngines)
+ {
+ this.proxyEngines.clear();
+ this.proxyEngines.putAll(proxyEngines);
+ }
+
+ public ProxyEngine getProxyEngine(String protocol)
+ {
+ return proxyEngines.get(protocol);
+ }
+
+ public void putProxyEngine(String protocol, ProxyEngine proxyEngine)
+ {
+ proxyEngines.put(protocol, proxyEngine);
+ }
+
+ public Map<String, ProxyServerInfo> getProxyServerInfos()
+ {
+ return new HashMap<>(proxyInfos);
+ }
+
+ protected ProxyServerInfo getProxyServerInfo(String host)
+ {
+ return proxyInfos.get(host);
+ }
+
+ public void setProxyServerInfos(Map<String, ProxyServerInfo> proxyServerInfos)
+ {
+ this.proxyInfos.clear();
+ this.proxyInfos.putAll(proxyServerInfos);
+ }
+
+ public void putProxyServerInfo(String host, ProxyServerInfo proxyServerInfo)
+ {
+ proxyInfos.put(host, proxyServerInfo);
+ }
+
+ private void rst(Stream stream)
+ {
+ RstInfo rstInfo = new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM);
+ stream.getSession().rst(rstInfo);
+ }
+
+ public static class ProxyServerInfo
+ {
+ private final String protocol;
+ private final String host;
+ private final InetSocketAddress address;
+
+ public ProxyServerInfo(String protocol, String host, int port)
+ {
+ this.protocol = protocol;
+ this.host = host;
+ this.address = new InetSocketAddress(host, port);
+ }
+
+ public String getProtocol()
+ {
+ return protocol;
+ }
+
+ public String getHost()
+ {
+ return host;
+ }
+
+ public InetSocketAddress getAddress()
+ {
+ return address;
+ }
+ }
+}
diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPAsyncConnectionFactory.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPAsyncConnectionFactory.java
new file mode 100644
index 0000000..f73c7e8
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPAsyncConnectionFactory.java
@@ -0,0 +1,41 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.proxy;
+
+import java.nio.channels.SocketChannel;
+
+import org.eclipse.jetty.io.AsyncEndPoint;
+import org.eclipse.jetty.io.nio.AsyncConnection;
+import org.eclipse.jetty.spdy.SPDYServerConnector;
+import org.eclipse.jetty.spdy.http.ServerHTTPAsyncConnectionFactory;
+
+public class ProxyHTTPAsyncConnectionFactory extends ServerHTTPAsyncConnectionFactory
+{
+ private final short version;
+ private final ProxyEngineSelector proxyEngineSelector;
+
+ public ProxyHTTPAsyncConnectionFactory(SPDYServerConnector connector, short version, ProxyEngineSelector proxyEngineSelector)
+ {
+ super(connector);
+ this.version = version;
+ this.proxyEngineSelector = proxyEngineSelector;
+ }
+
+ @Override
+ public AsyncConnection newAsyncConnection(SocketChannel channel, AsyncEndPoint endPoint, Object attachment)
+ {
+ return new ProxyHTTPSPDYAsyncConnection(getConnector(), endPoint, version, proxyEngineSelector);
+ }
+}
diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPSPDYAsyncConnection.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPSPDYAsyncConnection.java
new file mode 100644
index 0000000..92c3184
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPSPDYAsyncConnection.java
@@ -0,0 +1,337 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.proxy;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpGenerator;
+import org.eclipse.jetty.io.AsyncEndPoint;
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.ByteArrayBuffer;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.nio.DirectNIOBuffer;
+import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
+import org.eclipse.jetty.io.nio.NIOBuffer;
+import org.eclipse.jetty.server.AsyncHttpConnection;
+import org.eclipse.jetty.spdy.ISession;
+import org.eclipse.jetty.spdy.IStream;
+import org.eclipse.jetty.spdy.SPDYServerConnector;
+import org.eclipse.jetty.spdy.StandardSession;
+import org.eclipse.jetty.spdy.StandardStream;
+import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
+import org.eclipse.jetty.spdy.api.BytesDataInfo;
+import org.eclipse.jetty.spdy.api.DataInfo;
+import org.eclipse.jetty.spdy.api.GoAwayInfo;
+import org.eclipse.jetty.spdy.api.Handler;
+import org.eclipse.jetty.spdy.api.Headers;
+import org.eclipse.jetty.spdy.api.HeadersInfo;
+import org.eclipse.jetty.spdy.api.ReplyInfo;
+import org.eclipse.jetty.spdy.api.RstInfo;
+import org.eclipse.jetty.spdy.api.SessionStatus;
+import org.eclipse.jetty.spdy.api.Stream;
+import org.eclipse.jetty.spdy.api.StreamFrameListener;
+import org.eclipse.jetty.spdy.api.SynInfo;
+import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
+
+public class ProxyHTTPSPDYAsyncConnection extends AsyncHttpConnection
+{
+ private final Headers headers = new Headers();
+ private final short version;
+ private final ProxyEngineSelector proxyEngineSelector;
+ private final HttpGenerator generator;
+ private final ISession session;
+ private HTTPStream stream;
+ private Buffer content;
+
+ public ProxyHTTPSPDYAsyncConnection(SPDYServerConnector connector, EndPoint endPoint, short version, ProxyEngineSelector proxyEngineSelector)
+ {
+ super(connector, endPoint, connector.getServer());
+ this.version = version;
+ this.proxyEngineSelector = proxyEngineSelector;
+ this.generator = (HttpGenerator)_generator;
+ this.session = new HTTPSession(version, connector);
+ this.session.setAttribute("org.eclipse.jetty.spdy.remoteAddress", endPoint.getRemoteAddr());
+ }
+
+ @Override
+ public AsyncEndPoint getEndPoint()
+ {
+ return (AsyncEndPoint)super.getEndPoint();
+ }
+
+ @Override
+ protected void startRequest(Buffer method, Buffer uri, Buffer httpVersion) throws IOException
+ {
+ SPDYServerConnector connector = (SPDYServerConnector)getConnector();
+ String scheme = connector.getSslContextFactory() != null ? "https" : "http";
+ headers.put(HTTPSPDYHeader.SCHEME.name(version), scheme);
+ headers.put(HTTPSPDYHeader.METHOD.name(version), method.toString("UTF-8"));
+ headers.put(HTTPSPDYHeader.URI.name(version), uri.toString("UTF-8"));
+ headers.put(HTTPSPDYHeader.VERSION.name(version), httpVersion.toString("UTF-8"));
+ }
+
+ @Override
+ protected void parsedHeader(Buffer name, Buffer value) throws IOException
+ {
+ String headerName = name.toString("UTF-8").toLowerCase();
+ String headerValue = value.toString("UTF-8");
+ switch (headerName)
+ {
+ case "host":
+ headers.put(HTTPSPDYHeader.HOST.name(version), headerValue);
+ break;
+ default:
+ headers.put(headerName, headerValue);
+ break;
+ }
+ }
+
+ @Override
+ protected void headerComplete() throws IOException
+ {
+ }
+
+ @Override
+ protected void content(Buffer buffer) throws IOException
+ {
+ if (content == null)
+ {
+ stream = syn(false);
+ content = buffer;
+ }
+ else
+ {
+ stream.getStreamFrameListener().onData(stream, toDataInfo(buffer, false));
+ }
+ }
+
+ @Override
+ public void messageComplete(long contentLength) throws IOException
+ {
+ if (stream == null)
+ {
+ assert content == null;
+ if (headers.isEmpty())
+ proxyEngineSelector.onGoAway(session, new GoAwayInfo(0, SessionStatus.OK));
+ else
+ syn(true);
+ }
+ else
+ {
+ stream.getStreamFrameListener().onData(stream, toDataInfo(content, true));
+ }
+ headers.clear();
+ stream = null;
+ content = null;
+ }
+
+ private HTTPStream syn(boolean close)
+ {
+ HTTPStream stream = new HTTPStream(1, (byte)0, session, null);
+ StreamFrameListener streamFrameListener = proxyEngineSelector.onSyn(stream, new SynInfo(headers, close));
+ stream.setStreamFrameListener(streamFrameListener);
+ return stream;
+ }
+
+ private DataInfo toDataInfo(Buffer buffer, boolean close)
+ {
+ if (buffer instanceof ByteArrayBuffer)
+ return new BytesDataInfo(buffer.array(), buffer.getIndex(), buffer.length(), close);
+
+ if (buffer instanceof NIOBuffer)
+ {
+ ByteBuffer byteBuffer = ((NIOBuffer)buffer).getByteBuffer();
+ byteBuffer.limit(buffer.putIndex());
+ byteBuffer.position(buffer.getIndex());
+ return new ByteBufferDataInfo(byteBuffer, close);
+ }
+
+ return new BytesDataInfo(buffer.asArray(), close);
+ }
+
+ private class HTTPSession extends StandardSession
+ {
+ private HTTPSession(short version, SPDYServerConnector connector)
+ {
+ super(version, connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), null, null, 1, proxyEngineSelector, null, null);
+ }
+
+ @Override
+ public void rst(RstInfo rstInfo, long timeout, TimeUnit unit, Handler<Void> handler)
+ {
+ // Not much we can do in HTTP land: just close the connection
+ goAway(timeout, unit, handler);
+ }
+
+ @Override
+ public void goAway(long timeout, TimeUnit unit, Handler<Void> handler)
+ {
+ try
+ {
+ getEndPoint().close();
+ handler.completed(null);
+ }
+ catch (IOException x)
+ {
+ handler.failed(null, x);
+ }
+ }
+ }
+
+ /**
+ * <p>This stream will convert the SPDY invocations performed by the proxy into HTTP to be sent to the client.</p>
+ */
+ private class HTTPStream extends StandardStream
+ {
+ private final Pattern statusRegexp = Pattern.compile("(\\d{3})\\s*(.*)");
+
+ private HTTPStream(int id, byte priority, ISession session, IStream associatedStream)
+ {
+ super(id, priority, session, associatedStream);
+ }
+
+ @Override
+ public void syn(SynInfo synInfo, long timeout, TimeUnit unit, Handler<Stream> handler)
+ {
+ // HTTP does not support pushed streams
+ handler.completed(new HTTPPushStream(2, getPriority(), getSession(), this));
+ }
+
+ @Override
+ public void headers(HeadersInfo headersInfo, long timeout, TimeUnit unit, Handler<Void> handler)
+ {
+ // TODO
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ @Override
+ public void reply(ReplyInfo replyInfo, long timeout, TimeUnit unit, Handler<Void> handler)
+ {
+ try
+ {
+ Headers headers = new Headers(replyInfo.getHeaders(), false);
+
+ headers.remove(HTTPSPDYHeader.SCHEME.name(version));
+
+ String status = headers.remove(HTTPSPDYHeader.STATUS.name(version)).value();
+ Matcher matcher = statusRegexp.matcher(status);
+ matcher.matches();
+ int code = Integer.parseInt(matcher.group(1));
+ String reason = matcher.group(2);
+ generator.setResponse(code, reason);
+
+ String httpVersion = headers.remove(HTTPSPDYHeader.VERSION.name(version)).value();
+ generator.setVersion(Integer.parseInt(httpVersion.replaceAll("\\D", "")));
+
+ Headers.Header host = headers.remove(HTTPSPDYHeader.HOST.name(version));
+ if (host != null)
+ headers.put("host", host.value());
+
+ HttpFields fields = new HttpFields();
+ for (Headers.Header header : headers)
+ {
+ String name = camelize(header.name());
+ fields.put(name, header.value());
+ }
+ generator.completeHeader(fields, replyInfo.isClose());
+
+ if (replyInfo.isClose())
+ complete();
+
+ handler.completed(null);
+ }
+ catch (IOException x)
+ {
+ handler.failed(null, x);
+ }
+ }
+
+ private String camelize(String name)
+ {
+ char[] chars = name.toCharArray();
+ chars[0] = Character.toUpperCase(chars[0]);
+
+ for (int i = 0; i < chars.length; ++i)
+ {
+ char c = chars[i];
+ int j = i + 1;
+ if (c == '-' && j < chars.length)
+ chars[j] = Character.toUpperCase(chars[j]);
+ }
+ return new String(chars);
+ }
+
+ @Override
+ public void data(DataInfo dataInfo, long timeout, TimeUnit unit, Handler<Void> handler)
+ {
+ try
+ {
+ // Data buffer must be copied, as the ByteBuffer is pooled
+ ByteBuffer byteBuffer = dataInfo.asByteBuffer(false);
+
+ Buffer buffer = byteBuffer.isDirect() ?
+ new DirectNIOBuffer(byteBuffer, false) :
+ new IndirectNIOBuffer(byteBuffer, false);
+
+ generator.addContent(buffer, dataInfo.isClose());
+ generator.flush(unit.toMillis(timeout));
+
+ if (dataInfo.isClose())
+ complete();
+
+ handler.completed(null);
+ }
+ catch (IOException x)
+ {
+ handler.failed(null, x);
+ }
+ }
+
+ private void complete() throws IOException
+ {
+ generator.complete();
+ // We need to call asyncDispatch() as if the HTTP request
+ // has been suspended and now we complete the response
+ getEndPoint().asyncDispatch();
+ }
+ }
+
+ private class HTTPPushStream extends StandardStream
+ {
+ private HTTPPushStream(int id, byte priority, ISession session, IStream associatedStream)
+ {
+ super(id, priority, session, associatedStream);
+ }
+
+ @Override
+ public void headers(HeadersInfo headersInfo, long timeout, TimeUnit unit, Handler<Void> handler)
+ {
+ // Ignore pushed headers
+ handler.completed(null);
+ }
+
+ @Override
+ public void data(DataInfo dataInfo, long timeout, TimeUnit unit, Handler<Void> handler)
+ {
+ // Ignore pushed data
+ handler.completed(null);
+ }
+ }
+}
diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/SPDYProxyEngine.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/SPDYProxyEngine.java
new file mode 100644
index 0000000..c01af3e
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/SPDYProxyEngine.java
@@ -0,0 +1,514 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.proxy;
+
+import java.net.InetSocketAddress;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.spdy.SPDYClient;
+import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
+import org.eclipse.jetty.spdy.api.DataInfo;
+import org.eclipse.jetty.spdy.api.GoAwayInfo;
+import org.eclipse.jetty.spdy.api.Handler;
+import org.eclipse.jetty.spdy.api.Headers;
+import org.eclipse.jetty.spdy.api.HeadersInfo;
+import org.eclipse.jetty.spdy.api.ReplyInfo;
+import org.eclipse.jetty.spdy.api.RstInfo;
+import org.eclipse.jetty.spdy.api.SPDY;
+import org.eclipse.jetty.spdy.api.Session;
+import org.eclipse.jetty.spdy.api.SessionFrameListener;
+import org.eclipse.jetty.spdy.api.Stream;
+import org.eclipse.jetty.spdy.api.StreamFrameListener;
+import org.eclipse.jetty.spdy.api.StreamStatus;
+import org.eclipse.jetty.spdy.api.SynInfo;
+import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
+
+/**
+ * <p>{@link SPDYProxyEngine} implements a SPDY to SPDY proxy, that is, converts SPDY events received by
+ * clients into SPDY events for the servers.</p>
+ */
+public class SPDYProxyEngine extends ProxyEngine implements StreamFrameListener
+{
+ private static final String STREAM_HANDLER_ATTRIBUTE = "org.eclipse.jetty.spdy.http.proxy.streamHandler";
+ private static final String CLIENT_STREAM_ATTRIBUTE = "org.eclipse.jetty.spdy.http.proxy.clientStream";
+
+ private final ConcurrentMap<String, Session> serverSessions = new ConcurrentHashMap<>();
+ private final SessionFrameListener sessionListener = new ProxySessionFrameListener();
+ private final SPDYClient.Factory factory;
+ private volatile long connectTimeout = 15000;
+ private volatile long timeout = 60000;
+
+ public SPDYProxyEngine(SPDYClient.Factory factory)
+ {
+ this.factory = factory;
+ }
+
+ public long getConnectTimeout()
+ {
+ return connectTimeout;
+ }
+
+ public void setConnectTimeout(long connectTimeout)
+ {
+ this.connectTimeout = connectTimeout;
+ }
+
+ public long getTimeout()
+ {
+ return timeout;
+ }
+
+ public void setTimeout(long timeout)
+ {
+ this.timeout = timeout;
+ }
+
+ public StreamFrameListener proxy(final Stream clientStream, SynInfo clientSynInfo, ProxyEngineSelector.ProxyServerInfo proxyServerInfo)
+ {
+ Headers headers = new Headers(clientSynInfo.getHeaders(), false);
+
+ short serverVersion = getVersion(proxyServerInfo.getProtocol());
+ InetSocketAddress address = proxyServerInfo.getAddress();
+ Session serverSession = produceSession(proxyServerInfo.getHost(), serverVersion, address);
+ if (serverSession == null)
+ {
+ rst(clientStream);
+ return null;
+ }
+
+ final Session clientSession = clientStream.getSession();
+
+ addRequestProxyHeaders(clientStream, headers);
+ customizeRequestHeaders(clientStream, headers);
+ convert(clientSession.getVersion(), serverVersion, headers);
+
+ SynInfo serverSynInfo = new SynInfo(headers, clientSynInfo.isClose());
+ StreamFrameListener listener = new ProxyStreamFrameListener(clientStream);
+ StreamHandler handler = new StreamHandler(clientStream, serverSynInfo);
+ clientStream.setAttribute(STREAM_HANDLER_ATTRIBUTE, handler);
+ serverSession.syn(serverSynInfo, listener, timeout, TimeUnit.MILLISECONDS, handler);
+ return this;
+ }
+
+ private static short getVersion(String protocol)
+ {
+ switch (protocol)
+ {
+ case "spdy/2":
+ return SPDY.V2;
+ case "spdy/3":
+ return SPDY.V3;
+ default:
+ throw new IllegalArgumentException("Procotol: " + protocol + " is not a known SPDY protocol");
+ }
+ }
+
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ // Servers do not receive replies
+ }
+
+ @Override
+ public void onHeaders(Stream stream, HeadersInfo headersInfo)
+ {
+ // TODO
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ @Override
+ public void onData(Stream clientStream, final DataInfo clientDataInfo)
+ {
+ logger.debug("C -> P {} on {}", clientDataInfo, clientStream);
+
+ ByteBufferDataInfo serverDataInfo = new ByteBufferDataInfo(clientDataInfo.asByteBuffer(false), clientDataInfo.isClose())
+ {
+ @Override
+ public void consume(int delta)
+ {
+ super.consume(delta);
+ clientDataInfo.consume(delta);
+ }
+ };
+
+ StreamHandler streamHandler = (StreamHandler)clientStream.getAttribute(STREAM_HANDLER_ATTRIBUTE);
+ streamHandler.data(serverDataInfo);
+ }
+
+ private Session produceSession(String host, short version, InetSocketAddress address)
+ {
+ try
+ {
+ Session session = serverSessions.get(host);
+ if (session == null)
+ {
+ SPDYClient client = factory.newSPDYClient(version);
+ session = client.connect(address, sessionListener).get(getConnectTimeout(), TimeUnit.MILLISECONDS);
+ logger.debug("Proxy session connected to {}", address);
+ Session existing = serverSessions.putIfAbsent(host, session);
+ if (existing != null)
+ {
+ session.goAway(getTimeout(), TimeUnit.MILLISECONDS, new Handler.Adapter<Void>());
+ session = existing;
+ }
+ }
+ return session;
+ }
+ catch (Exception x)
+ {
+ logger.debug(x);
+ return null;
+ }
+ }
+
+ private void convert(short fromVersion, short toVersion, Headers headers)
+ {
+ if (fromVersion != toVersion)
+ {
+ for (HTTPSPDYHeader httpHeader : HTTPSPDYHeader.values())
+ {
+ Headers.Header header = headers.remove(httpHeader.name(fromVersion));
+ if (header != null)
+ {
+ String toName = httpHeader.name(toVersion);
+ for (String value : header.values())
+ headers.add(toName, value);
+ }
+ }
+ }
+ }
+
+ private void rst(Stream stream)
+ {
+ RstInfo rstInfo = new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM);
+ stream.getSession().rst(rstInfo, getTimeout(), TimeUnit.MILLISECONDS, new Handler.Adapter<Void>());
+ }
+
+ private class ProxyStreamFrameListener extends StreamFrameListener.Adapter
+ {
+ private final Stream clientStream;
+ private volatile ReplyInfo replyInfo;
+
+ public ProxyStreamFrameListener(Stream clientStream)
+ {
+ this.clientStream = clientStream;
+ }
+
+ @Override
+ public void onReply(final Stream stream, ReplyInfo replyInfo)
+ {
+ logger.debug("S -> P {} on {}", replyInfo, stream);
+
+ short serverVersion = stream.getSession().getVersion();
+ Headers headers = new Headers(replyInfo.getHeaders(), false);
+
+ addResponseProxyHeaders(stream, headers);
+ customizeResponseHeaders(stream, headers);
+ short clientVersion = this.clientStream.getSession().getVersion();
+ convert(serverVersion, clientVersion, headers);
+
+ this.replyInfo = new ReplyInfo(headers, replyInfo.isClose());
+ if (replyInfo.isClose())
+ reply(stream);
+ }
+
+ @Override
+ public void onHeaders(Stream stream, HeadersInfo headersInfo)
+ {
+ // TODO
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ @Override
+ public void onData(final Stream stream, final DataInfo dataInfo)
+ {
+ logger.debug("S -> P {} on {}", dataInfo, stream);
+
+ if (replyInfo != null)
+ {
+ if (dataInfo.isClose())
+ replyInfo.getHeaders().put("content-length", String.valueOf(dataInfo.available()));
+ reply(stream);
+ }
+ data(stream, dataInfo);
+ }
+
+ private void reply(final Stream stream)
+ {
+ final ReplyInfo replyInfo = this.replyInfo;
+ this.replyInfo = null;
+ clientStream.reply(replyInfo, getTimeout(), TimeUnit.MILLISECONDS, new Handler<Void>()
+ {
+ @Override
+ public void completed(Void context)
+ {
+ logger.debug("P -> C {} from {} to {}", replyInfo, stream, clientStream);
+ }
+
+ @Override
+ public void failed(Void context, Throwable x)
+ {
+ logger.debug(x);
+ rst(clientStream);
+ }
+ });
+ }
+
+ private void data(final Stream stream, final DataInfo dataInfo)
+ {
+ clientStream.data(dataInfo, getTimeout(), TimeUnit.MILLISECONDS, new Handler<Void>()
+ {
+ @Override
+ public void completed(Void context)
+ {
+ dataInfo.consume(dataInfo.length());
+ logger.debug("P -> C {} from {} to {}", dataInfo, stream, clientStream);
+ }
+
+ @Override
+ public void failed(Void context, Throwable x)
+ {
+ logger.debug(x);
+ rst(clientStream);
+ }
+ });
+ }
+ }
+
+ /**
+ * <p>{@link StreamHandler} implements the forwarding of DATA frames from the client to the server.</p>
+ * <p>Instances of this class buffer DATA frames sent by clients and send them to the server.
+ * The buffering happens between the send of the SYN_STREAM to the server (where DATA frames may arrive
+ * from the client before the SYN_STREAM has been fully sent), and between DATA frames, if the client
+ * is a fast producer and the server a slow consumer, or if the client is a SPDY v2 client (and hence
+ * without flow control) while the server is a SPDY v3 server (and hence with flow control).</p>
+ */
+ private class StreamHandler implements Handler<Stream>
+ {
+ private final Queue<DataInfoHandler> queue = new LinkedList<>();
+ private final Stream clientStream;
+ private final SynInfo serverSynInfo;
+ private Stream serverStream;
+
+ private StreamHandler(Stream clientStream, SynInfo serverSynInfo)
+ {
+ this.clientStream = clientStream;
+ this.serverSynInfo = serverSynInfo;
+ }
+
+ @Override
+ public void completed(Stream serverStream)
+ {
+ logger.debug("P -> S {} from {} to {}", serverSynInfo, clientStream, serverStream);
+
+ serverStream.setAttribute(CLIENT_STREAM_ATTRIBUTE, clientStream);
+
+ DataInfoHandler dataInfoHandler;
+ synchronized (queue)
+ {
+ this.serverStream = serverStream;
+ dataInfoHandler = queue.peek();
+ if (dataInfoHandler != null)
+ {
+ if (dataInfoHandler.flushing)
+ {
+ logger.debug("SYN completed, flushing {}, queue size {}", dataInfoHandler.dataInfo, queue.size());
+ dataInfoHandler = null;
+ }
+ else
+ {
+ dataInfoHandler.flushing = true;
+ logger.debug("SYN completed, queue size {}", queue.size());
+ }
+ }
+ else
+ {
+ logger.debug("SYN completed, queue empty");
+ }
+ }
+ if (dataInfoHandler != null)
+ flush(serverStream, dataInfoHandler);
+ }
+
+ @Override
+ public void failed(Stream serverStream, Throwable x)
+ {
+ logger.debug(x);
+ rst(clientStream);
+ }
+
+ public void data(DataInfo dataInfo)
+ {
+ Stream serverStream;
+ DataInfoHandler dataInfoHandler = null;
+ DataInfoHandler item = new DataInfoHandler(dataInfo);
+ synchronized (queue)
+ {
+ queue.offer(item);
+ serverStream = this.serverStream;
+ if (serverStream != null)
+ {
+ dataInfoHandler = queue.peek();
+ if (dataInfoHandler.flushing)
+ {
+ logger.debug("Queued {}, flushing {}, queue size {}", dataInfo, dataInfoHandler.dataInfo, queue.size());
+ serverStream = null;
+ }
+ else
+ {
+ dataInfoHandler.flushing = true;
+ logger.debug("Queued {}, queue size {}", dataInfo, queue.size());
+ }
+ }
+ else
+ {
+ logger.debug("Queued {}, SYN incomplete, queue size {}", dataInfo, queue.size());
+ }
+ }
+ if (serverStream != null)
+ flush(serverStream, dataInfoHandler);
+ }
+
+ private void flush(Stream serverStream, DataInfoHandler dataInfoHandler)
+ {
+ logger.debug("P -> S {} on {}", dataInfoHandler.dataInfo, serverStream);
+ serverStream.data(dataInfoHandler.dataInfo, getTimeout(), TimeUnit.MILLISECONDS, dataInfoHandler);
+ }
+
+ private class DataInfoHandler implements Handler<Void>
+ {
+ private final DataInfo dataInfo;
+ private boolean flushing;
+
+ private DataInfoHandler(DataInfo dataInfo)
+ {
+ this.dataInfo = dataInfo;
+ }
+
+ @Override
+ public void completed(Void context)
+ {
+ Stream serverStream;
+ DataInfoHandler dataInfoHandler;
+ synchronized (queue)
+ {
+ serverStream = StreamHandler.this.serverStream;
+ assert serverStream != null;
+ dataInfoHandler = queue.poll();
+ assert dataInfoHandler == this;
+ dataInfoHandler = queue.peek();
+ if (dataInfoHandler != null)
+ {
+ assert !dataInfoHandler.flushing;
+ dataInfoHandler.flushing = true;
+ logger.debug("Completed {}, queue size {}", dataInfo, queue.size());
+ }
+ else
+ {
+ logger.debug("Completed {}, queue empty", dataInfo);
+ }
+ }
+ if (dataInfoHandler != null)
+ flush(serverStream, dataInfoHandler);
+ }
+
+ @Override
+ public void failed(Void context, Throwable x)
+ {
+ logger.debug(x);
+ rst(clientStream);
+ }
+ }
+ }
+
+ private class ProxySessionFrameListener extends SessionFrameListener.Adapter implements StreamFrameListener
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream serverStream, SynInfo serverSynInfo)
+ {
+ logger.debug("S -> P pushed {} on {}", serverSynInfo, serverStream);
+
+ Headers headers = new Headers(serverSynInfo.getHeaders(), false);
+
+ addResponseProxyHeaders(serverStream, headers);
+ customizeResponseHeaders(serverStream, headers);
+ Stream clientStream = (Stream)serverStream.getAssociatedStream().getAttribute(CLIENT_STREAM_ATTRIBUTE);
+ convert(serverStream.getSession().getVersion(), clientStream.getSession().getVersion(), headers);
+
+ StreamHandler handler = new StreamHandler(clientStream, serverSynInfo);
+ serverStream.setAttribute(STREAM_HANDLER_ATTRIBUTE, handler);
+ clientStream.syn(new SynInfo(headers, serverSynInfo.isClose()), getTimeout(), TimeUnit.MILLISECONDS, handler);
+
+ return this;
+ }
+
+ @Override
+ public void onRst(Session serverSession, RstInfo serverRstInfo)
+ {
+ Stream serverStream = serverSession.getStream(serverRstInfo.getStreamId());
+ if (serverStream != null)
+ {
+ Stream clientStream = (Stream)serverStream.getAttribute(CLIENT_STREAM_ATTRIBUTE);
+ if (clientStream != null)
+ {
+ Session clientSession = clientStream.getSession();
+ RstInfo clientRstInfo = new RstInfo(clientStream.getId(), serverRstInfo.getStreamStatus());
+ clientSession.rst(clientRstInfo, getTimeout(), TimeUnit.MILLISECONDS, new Handler.Adapter<Void>());
+ }
+ }
+ }
+
+ @Override
+ public void onGoAway(Session serverSession, GoAwayInfo goAwayInfo)
+ {
+ serverSessions.values().remove(serverSession);
+ }
+
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ // Push streams never send a reply
+ }
+
+ @Override
+ public void onHeaders(Stream stream, HeadersInfo headersInfo)
+ {
+ throw new UnsupportedOperationException(); //TODO
+ }
+
+ @Override
+ public void onData(Stream serverStream, final DataInfo serverDataInfo)
+ {
+ logger.debug("S -> P pushed {} on {}", serverDataInfo, serverStream);
+
+ ByteBufferDataInfo clientDataInfo = new ByteBufferDataInfo(serverDataInfo.asByteBuffer(false), serverDataInfo.isClose())
+ {
+ @Override
+ public void consume(int delta)
+ {
+ super.consume(delta);
+ serverDataInfo.consume(delta);
+ }
+ };
+
+ StreamHandler handler = (StreamHandler)serverStream.getAttribute(STREAM_HANDLER_ATTRIBUTE);
+ handler.data(clientDataInfo);
+ }
+ }
+}
diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/AbstractHTTPSPDYTest.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/AbstractHTTPSPDYTest.java
index 84dd327..a5c4c28 100644
--- a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/AbstractHTTPSPDYTest.java
+++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/AbstractHTTPSPDYTest.java
@@ -1,18 +1,16 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.http;
@@ -55,8 +53,13 @@ public abstract class AbstractHTTPSPDYTest
protected InetSocketAddress startHTTPServer(Handler handler) throws Exception
{
+ return startHTTPServer(SPDY.V2, handler);
+ }
+
+ protected InetSocketAddress startHTTPServer(short version, Handler handler) throws Exception
+ {
server = new Server();
- connector = newHTTPSPDYServerConnector();
+ connector = newHTTPSPDYServerConnector(version);
connector.setPort(0);
server.addConnector(connector);
server.setHandler(handler);
@@ -64,21 +67,22 @@ public abstract class AbstractHTTPSPDYTest
return new InetSocketAddress("localhost", connector.getLocalPort());
}
- protected SPDYServerConnector newHTTPSPDYServerConnector()
+ protected SPDYServerConnector newHTTPSPDYServerConnector(short version)
{
// For these tests, we need the connector to speak HTTP over SPDY even in non-SSL
- return new HTTPSPDYServerConnector()
- {
- @Override
- protected AsyncConnectionFactory getDefaultAsyncConnectionFactory()
- {
- return new ServerHTTPSPDYAsyncConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), this, new PushStrategy.None());
- }
- };
+ SPDYServerConnector connector = new HTTPSPDYServerConnector();
+ AsyncConnectionFactory defaultFactory = new ServerHTTPSPDYAsyncConnectionFactory(version, connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, new PushStrategy.None());
+ connector.setDefaultAsyncConnectionFactory(defaultFactory);
+ return connector;
}
protected Session startClient(InetSocketAddress socketAddress, SessionFrameListener listener) throws Exception
{
+ return startClient(SPDY.V2, socketAddress, listener);
+ }
+
+ protected Session startClient(short version, InetSocketAddress socketAddress, SessionFrameListener listener) throws Exception
+ {
if (clientFactory == null)
{
QueuedThreadPool threadPool = new QueuedThreadPool();
@@ -86,7 +90,7 @@ public abstract class AbstractHTTPSPDYTest
clientFactory = newSPDYClientFactory(threadPool);
clientFactory.start();
}
- return clientFactory.newSPDYClient(SPDY.V2).connect(socketAddress, listener).get(5, TimeUnit.SECONDS);
+ return clientFactory.newSPDYClient(version).connect(socketAddress, listener).get(5, TimeUnit.SECONDS);
}
protected SPDYClient.Factory newSPDYClientFactory(Executor threadPool)
@@ -107,4 +111,9 @@ public abstract class AbstractHTTPSPDYTest
server.join();
}
}
+
+ protected short version()
+ {
+ return SPDY.V2;
+ }
}
diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ConcurrentStreamsTest.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ConcurrentStreamsTest.java
index a265550..56cb5ef 100644
--- a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ConcurrentStreamsTest.java
+++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ConcurrentStreamsTest.java
@@ -1,18 +1,16 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.http;
@@ -73,10 +71,10 @@ public class ConcurrentStreamsTest extends AbstractHTTPSPDYTest
// Perform slow request. This will wait on server side until the fast request wakes it up
Headers headers = new Headers();
- headers.put("method", "GET");
- headers.put("url", "/slow");
- headers.put("version", "HTTP/1.1");
- headers.put("host", "localhost:" + connector.getLocalPort());
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/slow");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
final CountDownLatch slowClientLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
@@ -91,10 +89,10 @@ public class ConcurrentStreamsTest extends AbstractHTTPSPDYTest
// Perform the fast request. This will wake up the slow request
headers.clear();
- headers.put("method", "GET");
- headers.put("url", "/fast");
- headers.put("version", "HTTP/1.1");
- headers.put("host", "localhost:" + connector.getLocalPort());
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/fast");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
final CountDownLatch fastClientLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ProtocolNegotiationTest.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ProtocolNegotiationTest.java
index e9cca9d..5de439b 100644
--- a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ProtocolNegotiationTest.java
+++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ProtocolNegotiationTest.java
@@ -1,18 +1,16 @@
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+//========================================================================
+//Copyright 2011-2012 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.spdy.http;
@@ -27,7 +25,6 @@ import javax.net.ssl.SSLSocket;
import org.eclipse.jetty.npn.NextProtoNego;
import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.spdy.AsyncConnectionFactory;
import org.eclipse.jetty.spdy.SPDYServerConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.Assert;
@@ -109,9 +106,8 @@ public class ProtocolNegotiationTest
public String selectProtocol(List<String> strings)
{
Assert.assertNotNull(strings);
- Assert.assertEquals(1, strings.size());
- String protocol = strings.get(0);
- Assert.assertEquals("http/1.1", protocol);
+ String protocol = "http/1.1";
+ Assert.assertTrue(strings.contains(protocol));
return protocol;
}
});
@@ -166,11 +162,11 @@ public class ProtocolNegotiationTest
public String selectProtocol(List<String> strings)
{
Assert.assertNotNull(strings);
- Assert.assertEquals(2, strings.size());
- String spdyProtocol = strings.get(0);
- Assert.assertEquals("spdy/2", spdyProtocol);
- String httpProtocol = strings.get(1);
- Assert.assertEquals("http/1.1", httpProtocol);
+ String spdyProtocol = "spdy/2";
+ Assert.assertTrue(strings.contains(spdyProtocol));
+ String httpProtocol = "http/1.1";
+ Assert.assertTrue(strings.contains(httpProtocol));
+ Assert.assertTrue(strings.indexOf(spdyProtocol) < strings.indexOf(httpProtocol));
return httpProtocol;
}
});
@@ -198,14 +194,9 @@ public class ProtocolNegotiationTest
@Test
public void testServerAdvertisingSPDYAndHTTPSpeaksDefaultProtocolWhenNPNMissing() throws Exception
{
- InetSocketAddress address = startServer(new SPDYServerConnector(null, newSslContextFactory())
- {
- @Override
- protected AsyncConnectionFactory getDefaultAsyncConnectionFactory()
- {
- return new ServerHTTPAsyncConnectionFactory(connector);
- }
- });
+ SPDYServerConnector connector = new SPDYServerConnector(null, newSslContextFactory());
+ connector.setDefaultAsyncConnectionFactory(new ServerHTTPAsyncConnectionFactory(connector));
+ InetSocketAddress address = startServer(connector);
connector.putAsyncConnectionFactory("http/1.1", new ServerHTTPAsyncConnectionFactory(connector));
SslContextFactory sslContextFactory = newSslContextFactory();
diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/PushStrategyBenchmarkTest.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/PushStrategyBenchmarkTest.java
new file mode 100644
index 0000000..9486157
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/PushStrategyBenchmarkTest.java
@@ -0,0 +1,395 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.http;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.Assert;
+import org.eclipse.jetty.client.Address;
+import org.eclipse.jetty.client.ContentExchange;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpExchange;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.spdy.AsyncConnectionFactory;
+import org.eclipse.jetty.spdy.api.DataInfo;
+import org.eclipse.jetty.spdy.api.Headers;
+import org.eclipse.jetty.spdy.api.SPDY;
+import org.eclipse.jetty.spdy.api.Session;
+import org.eclipse.jetty.spdy.api.SessionFrameListener;
+import org.eclipse.jetty.spdy.api.Stream;
+import org.eclipse.jetty.spdy.api.StreamFrameListener;
+import org.eclipse.jetty.spdy.api.SynInfo;
+import org.junit.Test;
+
+public class PushStrategyBenchmarkTest extends AbstractHTTPSPDYTest
+{
+ // Sample resources size from webtide.com home page
+ private final int[] htmlResources = new int[]
+ {8 * 1024};
+ private final int[] cssResources = new int[]
+ {12 * 1024, 2 * 1024};
+ private final int[] jsResources = new int[]
+ {75 * 1024, 24 * 1024, 36 * 1024};
+ private final int[] pngResources = new int[]
+ {1024, 45 * 1024, 6 * 1024, 2 * 1024, 2 * 1024, 2 * 1024, 3 * 1024, 512, 512, 19 * 1024, 512, 128, 32};
+ private final Set<String> pushedResources = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
+ private final AtomicReference<CountDownLatch> latch = new AtomicReference<>();
+ private final long roundtrip = 100;
+ private final int runs = 10;
+
+ @Test
+ public void benchmarkPushStrategy() throws Exception
+ {
+ InetSocketAddress address = startHTTPServer(version(), new PushStrategyBenchmarkHandler());
+
+ // Plain HTTP
+ AsyncConnectionFactory dacf = new ServerHTTPAsyncConnectionFactory(connector);
+ connector.setDefaultAsyncConnectionFactory(dacf);
+ HttpClient httpClient = new HttpClient();
+ // Simulate browsers, that open 6 connection per origin
+ httpClient.setMaxConnectionsPerAddress(6);
+ httpClient.start();
+ benchmarkHTTP(httpClient);
+ httpClient.stop();
+
+ // First push strategy
+ PushStrategy pushStrategy = new PushStrategy.None();
+ dacf = new ServerHTTPSPDYAsyncConnectionFactory(version(), connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, pushStrategy);
+ connector.setDefaultAsyncConnectionFactory(dacf);
+ Session session = startClient(version(), address, new ClientSessionFrameListener());
+ benchmarkSPDY(pushStrategy, session);
+ session.goAway().get(5, TimeUnit.SECONDS);
+
+ // Second push strategy
+ pushStrategy = new ReferrerPushStrategy();
+ dacf = new ServerHTTPSPDYAsyncConnectionFactory(version(), connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, pushStrategy);
+ connector.setDefaultAsyncConnectionFactory(dacf);
+ session = startClient(version(), address, new ClientSessionFrameListener());
+ benchmarkSPDY(pushStrategy, session);
+ session.goAway().get(5, TimeUnit.SECONDS);
+ }
+
+ private void benchmarkHTTP(HttpClient httpClient) throws Exception
+ {
+ // Warm up
+ performHTTPRequests(httpClient);
+ performHTTPRequests(httpClient);
+
+ long total = 0;
+ for (int i = 0; i < runs; ++i)
+ {
+ long begin = System.nanoTime();
+ int requests = performHTTPRequests(httpClient);
+ long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - begin);
+ total += elapsed;
+ System.err.printf("HTTP: run %d, %d request(s), roundtrip delay %d ms, elapsed = %d%n",
+ i, requests, roundtrip, elapsed);
+ }
+ System.err.printf("HTTP: roundtrip delay %d ms, average = %d%n%n",
+ roundtrip, total / runs);
+ }
+
+ private int performHTTPRequests(HttpClient httpClient) throws Exception
+ {
+ int result = 0;
+
+ for (int j = 0; j < htmlResources.length; ++j)
+ {
+ latch.set(new CountDownLatch(cssResources.length + jsResources.length + pngResources.length));
+
+ String primaryPath = "/" + j + ".html";
+ String referrer = new StringBuilder("http://localhost:").append(connector.getLocalPort()).append(primaryPath).toString();
+ ContentExchange exchange = new ContentExchange(true);
+ exchange.setMethod("GET");
+ exchange.setRequestURI(primaryPath);
+ exchange.setVersion("HTTP/1.1");
+ exchange.setAddress(new Address("localhost", connector.getLocalPort()));
+ exchange.setRequestHeader("Host", "localhost:" + connector.getLocalPort());
+ ++result;
+ httpClient.send(exchange);
+ Assert.assertEquals(HttpExchange.STATUS_COMPLETED, exchange.waitForDone());
+ Assert.assertEquals(200, exchange.getResponseStatus());
+
+ for (int i = 0; i < cssResources.length; ++i)
+ {
+ String path = "/" + i + ".css";
+ exchange = createExchangeWithReferrer(referrer, path);
+ ++result;
+ httpClient.send(exchange);
+ }
+ for (int i = 0; i < jsResources.length; ++i)
+ {
+ String path = "/" + i + ".js";
+ exchange = createExchangeWithReferrer(referrer, path);
+ ++result;
+ httpClient.send(exchange);
+ }
+ for (int i = 0; i < pngResources.length; ++i)
+ {
+ String path = "/" + i + ".png";
+ exchange = createExchangeWithReferrer(referrer, path);
+ ++result;
+ httpClient.send(exchange);
+ }
+
+ Assert.assertTrue(latch.get().await(5, TimeUnit.SECONDS));
+ }
+
+ return result;
+ }
+
+ private ContentExchange createExchangeWithReferrer(String referrer, String path)
+ {
+ ContentExchange exchange;
+ exchange = new TestExchange();
+ exchange.setMethod("GET");
+ exchange.setRequestURI(path);
+ exchange.setVersion("HTTP/1.1");
+ exchange.setAddress(new Address("localhost", connector.getLocalPort()));
+ exchange.setRequestHeader("Host", "localhost:" + connector.getLocalPort());
+ exchange.setRequestHeader("referer", referrer);
+ return exchange;
+ }
+
+
+ private void benchmarkSPDY(PushStrategy pushStrategy, Session session) throws Exception
+ {
+ // Warm up PushStrategy
+ performRequests(session);
+ performRequests(session);
+
+ long total = 0;
+ for (int i = 0; i < runs; ++i)
+ {
+ long begin = System.nanoTime();
+ int requests = performRequests(session);
+ long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - begin);
+ total += elapsed;
+ System.err.printf("SPDY(%s): run %d, %d request(s), roundtrip delay %d ms, elapsed = %d%n",
+ pushStrategy.getClass().getSimpleName(), i, requests, roundtrip, elapsed);
+ }
+ System.err.printf("SPDY(%s): roundtrip delay %d ms, average = %d%n%n",
+ pushStrategy.getClass().getSimpleName(), roundtrip, total / runs);
+ }
+
+ private int performRequests(Session session) throws Exception
+ {
+ int result = 0;
+
+ for (int j = 0; j < htmlResources.length; ++j)
+ {
+ latch.set(new CountDownLatch(cssResources.length + jsResources.length + pngResources.length));
+ pushedResources.clear();
+
+ String primaryPath = "/" + j + ".html";
+ String referrer = new StringBuilder("http://localhost:").append(connector.getLocalPort()).append(primaryPath).toString();
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), primaryPath);
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ // Wait for the HTML to simulate browser's behavior
+ ++result;
+ final CountDownLatch htmlLatch = new CountDownLatch(1);
+ session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ htmlLatch.countDown();
+ }
+ });
+ Assert.assertTrue(htmlLatch.await(5, TimeUnit.SECONDS));
+
+ for (int i = 0; i < cssResources.length; ++i)
+ {
+ String path = "/" + i + ".css";
+ if (pushedResources.contains(path))
+ continue;
+ headers = createRequestHeaders(referrer, path);
+ ++result;
+ session.syn(new SynInfo(headers, true), new DataListener());
+ }
+ for (int i = 0; i < jsResources.length; ++i)
+ {
+ String path = "/" + i + ".js";
+ if (pushedResources.contains(path))
+ continue;
+ headers = createRequestHeaders(referrer, path);
+ ++result;
+ session.syn(new SynInfo(headers, true), new DataListener());
+ }
+ for (int i = 0; i < pngResources.length; ++i)
+ {
+ String path = "/" + i + ".png";
+ if (pushedResources.contains(path))
+ continue;
+ headers = createRequestHeaders(referrer, path);
+ ++result;
+ session.syn(new SynInfo(headers, true), new DataListener());
+ }
+
+ Assert.assertTrue(latch.get().await(5, TimeUnit.SECONDS));
+ }
+
+ return result;
+ }
+
+ private Headers createRequestHeaders(String referrer, String path)
+ {
+ Headers headers;
+ headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), path);
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ headers.put("referer", referrer);
+ return headers;
+ }
+
+ private void sleep(long delay) throws ServletException
+ {
+ try
+ {
+ TimeUnit.MILLISECONDS.sleep(delay);
+ }
+ catch (InterruptedException x)
+ {
+ throw new ServletException(x);
+ }
+ }
+
+ private class PushStrategyBenchmarkHandler extends AbstractHandler
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+
+ // Sleep half of the roundtrip time, to simulate the delay of responses, even for pushed resources
+ sleep(roundtrip / 2);
+ // If it's not a pushed resource, sleep half of the roundtrip time, to simulate the delay of requests
+ if (request.getHeader("x-spdy-push") == null)
+ sleep(roundtrip / 2);
+
+ String suffix = target.substring(target.indexOf('.') + 1);
+ int index = Integer.parseInt(target.substring(1, target.length() - suffix.length() - 1));
+
+ int contentLength;
+ String contentType;
+ switch (suffix)
+ {
+ case "html":
+ contentLength = htmlResources[index];
+ contentType = "text/html";
+ break;
+ case "css":
+ contentLength = cssResources[index];
+ contentType = "text/css";
+ break;
+ case "js":
+ contentLength = jsResources[index];
+ contentType = "text/javascript";
+ break;
+ case "png":
+ contentLength = pngResources[index];
+ contentType = "image/png";
+ break;
+ default:
+ throw new ServletException();
+ }
+
+ response.setContentType(contentType);
+ response.setContentLength(contentLength);
+ response.getOutputStream().write(new byte[contentLength]);
+ }
+ }
+
+ private void addPushedResource(String pushedURI)
+ {
+ switch (version())
+ {
+ case SPDY.V2:
+ {
+ Matcher matcher = Pattern.compile("https?://[^:]+:\\d+(/.*)").matcher(pushedURI);
+ Assert.assertTrue(matcher.matches());
+ pushedResources.add(matcher.group(1));
+ break;
+ }
+ case SPDY.V3:
+ {
+ pushedResources.add(pushedURI);
+ break;
+ }
+ default:
+ {
+ throw new IllegalStateException();
+ }
+ }
+ }
+
+ private class ClientSessionFrameListener extends SessionFrameListener.Adapter
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
+ {
+ String path = synInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version())).value();
+ addPushedResource(path);
+ return new DataListener();
+ }
+ }
+
+ private class DataListener extends StreamFrameListener.Adapter
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ latch.get().countDown();
+ }
+ }
+
+ private class TestExchange extends ContentExchange
+ {
+ private TestExchange()
+ {
+ super(true);
+ }
+
+ @Override
+ protected void onResponseComplete() throws IOException
+ {
+ latch.get().countDown();
+ }
+ }
+}
diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyUnitTest.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyUnitTest.java
new file mode 100644
index 0000000..0edbcf8
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyUnitTest.java
@@ -0,0 +1,119 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.http;
+
+import java.util.Set;
+
+import org.eclipse.jetty.spdy.api.Headers;
+import org.eclipse.jetty.spdy.api.SPDY;
+import org.eclipse.jetty.spdy.api.Session;
+import org.eclipse.jetty.spdy.api.Stream;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ReferrerPushStrategyUnitTest
+{
+ public static final short VERSION = SPDY.V3;
+ public static final String SCHEME = "http";
+ public static final String HOST = "localhost";
+ public static final String MAIN_URI = "/index.html";
+ public static final String METHOD = "GET";
+
+ // class under test
+ private ReferrerPushStrategy referrerPushStrategy;
+
+ @Mock
+ Stream stream;
+ @Mock
+ Session session;
+
+
+ @Before
+ public void setup()
+ {
+ referrerPushStrategy = new ReferrerPushStrategy();
+ }
+
+ @Test
+ public void testReferrerCallsAfterTimeoutAreNotAddedAsPushResources() throws InterruptedException
+ {
+ Headers requestHeaders = getBaseHeaders(VERSION);
+ int referrerCallTimeout = 1000;
+ referrerPushStrategy.setReferrerPushPeriod(referrerCallTimeout);
+ setMockExpectations();
+
+ String referrerUrl = fillPushStrategyCache(requestHeaders);
+ Set<String> pushResources;
+
+ // sleep to pretend that the user manually clicked on a linked resource instead the browser requesting subresources immediately
+ Thread.sleep(referrerCallTimeout + 1);
+
+ requestHeaders.put(HTTPSPDYHeader.URI.name(VERSION), "image2.jpg");
+ requestHeaders.put("referer", referrerUrl);
+ pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Headers());
+ assertThat("pushResources is empty", pushResources.size(), is(0));
+
+ requestHeaders.put(HTTPSPDYHeader.URI.name(VERSION), MAIN_URI);
+ pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Headers());
+ // as the image2.jpg request has been a link and not a subresource, we expect that pushResources.size() is still 2
+ assertThat("pushResources contains two elements image.jpg and style.css", pushResources.size(), is(2));
+ }
+
+ private Headers getBaseHeaders(short version)
+ {
+ Headers requestHeaders = new Headers();
+ requestHeaders.put(HTTPSPDYHeader.SCHEME.name(version), SCHEME);
+ requestHeaders.put(HTTPSPDYHeader.HOST.name(version), HOST);
+ requestHeaders.put(HTTPSPDYHeader.URI.name(version), MAIN_URI);
+ requestHeaders.put(HTTPSPDYHeader.METHOD.name(version), METHOD);
+ return requestHeaders;
+ }
+
+ private void setMockExpectations()
+ {
+ when(stream.getSession()).thenReturn(session);
+ when(session.getVersion()).thenReturn(VERSION);
+ }
+
+ private String fillPushStrategyCache(Headers requestHeaders)
+ {
+ Set<String> pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Headers());
+ assertThat("pushResources is empty", pushResources.size(), is(0));
+
+ String origin = SCHEME + "://" + HOST;
+ String referrerUrl = origin + MAIN_URI;
+
+ requestHeaders.put(HTTPSPDYHeader.URI.name(VERSION), "image.jpg");
+ requestHeaders.put("referer", referrerUrl);
+ pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Headers());
+ assertThat("pushResources is empty", pushResources.size(), is(0));
+
+ requestHeaders.put(HTTPSPDYHeader.URI.name(VERSION), "style.css");
+ pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Headers());
+ assertThat("pushResources is empty", pushResources.size(), is(0));
+
+ requestHeaders.put(HTTPSPDYHeader.URI.name(VERSION), MAIN_URI);
+ pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Headers());
+ assertThat("pushResources contains two elements image.jpg and style.css", pushResources.size(), is(2));
+ return referrerUrl;
+ }
+}
diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyV2Test.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyV2Test.java
new file mode 100644
index 0000000..8bc79c3
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyV2Test.java
@@ -0,0 +1,797 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.http;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.InetSocketAddress;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.spdy.AsyncConnectionFactory;
+import org.eclipse.jetty.spdy.SPDYServerConnector;
+import org.eclipse.jetty.spdy.api.DataInfo;
+import org.eclipse.jetty.spdy.api.Headers;
+import org.eclipse.jetty.spdy.api.ReplyInfo;
+import org.eclipse.jetty.spdy.api.SPDY;
+import org.eclipse.jetty.spdy.api.Session;
+import org.eclipse.jetty.spdy.api.SessionFrameListener;
+import org.eclipse.jetty.spdy.api.Stream;
+import org.eclipse.jetty.spdy.api.StreamFrameListener;
+import org.eclipse.jetty.spdy.api.SynInfo;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest
+{
+
+ private final String mainResource = "/index.html";
+ private final String cssResource = "/style.css";
+
+ @Override
+ protected SPDYServerConnector newHTTPSPDYServerConnector(short version)
+ {
+ SPDYServerConnector connector = super.newHTTPSPDYServerConnector(version);
+ AsyncConnectionFactory defaultFactory = new ServerHTTPSPDYAsyncConnectionFactory(version, connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, new ReferrerPushStrategy());
+ connector.setDefaultAsyncConnectionFactory(defaultFactory);
+ return connector;
+ }
+
+ @Test
+ public void testPushHeadersAreValid() throws Exception
+ {
+ InetSocketAddress address = createServer();
+
+ ReferrerPushStrategy pushStrategy = new ReferrerPushStrategy();
+ int referrerPushPeriod = 1000;
+ pushStrategy.setReferrerPushPeriod(referrerPushPeriod);
+ AsyncConnectionFactory defaultFactory = new ServerHTTPSPDYAsyncConnectionFactory(version(), connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, pushStrategy);
+ connector.setDefaultAsyncConnectionFactory(defaultFactory);
+
+ Headers mainRequestHeaders = createHeadersWithoutReferrer(mainResource);
+ Session session1 = sendMainRequestAndCSSRequest(address, mainRequestHeaders);
+
+ // Sleep for pushPeriod This should prevent application.js from being mapped as pushResource
+ Thread.sleep(referrerPushPeriod + 1);
+
+ sendJSRequest(session1);
+
+ run2ndClientRequests(address, mainRequestHeaders, true);
+ }
+
+ @Test
+ public void testReferrerPushPeriod() throws Exception
+ {
+ InetSocketAddress address = createServer();
+
+ ReferrerPushStrategy pushStrategy = new ReferrerPushStrategy();
+ int referrerPushPeriod = 1000;
+ pushStrategy.setReferrerPushPeriod(referrerPushPeriod);
+ AsyncConnectionFactory defaultFactory = new ServerHTTPSPDYAsyncConnectionFactory(version(), connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, pushStrategy);
+ connector.setDefaultAsyncConnectionFactory(defaultFactory);
+
+ Headers mainRequestHeaders = createHeadersWithoutReferrer(mainResource);
+ Session session1 = sendMainRequestAndCSSRequest(address, mainRequestHeaders);
+
+ // Sleep for pushPeriod This should prevent application.js from being mapped as pushResource
+ Thread.sleep(referrerPushPeriod+1);
+
+ sendJSRequest(session1);
+
+ run2ndClientRequests(address, mainRequestHeaders, false);
+ }
+
+ @Test
+ public void testMaxAssociatedResources() throws Exception
+ {
+ InetSocketAddress address = createServer();
+
+ ReferrerPushStrategy pushStrategy = new ReferrerPushStrategy();
+ pushStrategy.setMaxAssociatedResources(1);
+ AsyncConnectionFactory defaultFactory = new ServerHTTPSPDYAsyncConnectionFactory(version(), connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, pushStrategy);
+ connector.setDefaultAsyncConnectionFactory(defaultFactory);
+
+ Headers mainRequestHeaders = createHeadersWithoutReferrer(mainResource);
+ Session session1 = sendMainRequestAndCSSRequest(address, mainRequestHeaders);
+
+ sendJSRequest(session1);
+
+ run2ndClientRequests(address, mainRequestHeaders, false);
+ }
+
+ private InetSocketAddress createServer() throws Exception
+ {
+ return startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ String url = request.getRequestURI();
+ PrintWriter output = response.getWriter();
+ if (url.endsWith(".html"))
+ output.print("<html><head/><body>HELLO</body></html>");
+ else if (url.endsWith(".css"))
+ output.print("body { background: #FFF; }");
+ else if (url.endsWith(".js"))
+ output.print("function(){}();");
+ baseRequest.setHandled(true);
+ }
+ });
+ }
+
+ private Session sendMainRequestAndCSSRequest(InetSocketAddress address, Headers mainRequestHeaders) throws Exception
+ {
+ Session session1 = startClient(version(), address, null);
+
+ final CountDownLatch mainResourceLatch = new CountDownLatch(1);
+ session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
+
+ final CountDownLatch associatedResourceLatch1 = new CountDownLatch(1);
+ Headers associatedRequestHeaders1 = createHeaders(cssResource);
+ session1.syn(new SynInfo(associatedRequestHeaders1, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ associatedResourceLatch1.countDown();
+ }
+ });
+ Assert.assertTrue(associatedResourceLatch1.await(5, TimeUnit.SECONDS));
+ return session1;
+ }
+
+
+ private void sendJSRequest(Session session1) throws InterruptedException
+ {
+ final CountDownLatch associatedResourceLatch2 = new CountDownLatch(1);
+ String jsResource = "/application.js";
+ Headers associatedRequestHeaders2 = createHeaders(jsResource);
+ session1.syn(new SynInfo(associatedRequestHeaders2, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ associatedResourceLatch2.countDown();
+ }
+ });
+ Assert.assertTrue(associatedResourceLatch2.await(5, TimeUnit.SECONDS));
+ }
+
+ private void run2ndClientRequests(InetSocketAddress address, Headers mainRequestHeaders, final boolean validateHeaders) throws Exception
+ {
+ // Create another client, and perform the same request for the main resource,
+ // we expect the css being pushed, but not the js
+
+ final CountDownLatch mainStreamLatch = new CountDownLatch(2);
+ final CountDownLatch pushDataLatch = new CountDownLatch(1);
+ final CountDownLatch pushSynHeadersValid = new CountDownLatch(1);
+ Session session2 = startClient(version(), address, new SessionFrameListener.Adapter()
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
+ {
+ if(validateHeaders)
+ validateHeaders(synInfo.getHeaders(), pushSynHeadersValid);
+
+ Assert.assertTrue(stream.isUnidirectional());
+ Assert.assertTrue(synInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version())).value().endsWith(".css"));
+ return new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ pushDataLatch.countDown();
+ }
+ };
+ }
+ });
+ session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ mainStreamLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainStreamLatch.countDown();
+ }
+ });
+
+ Assert.assertTrue("Main request reply and/or data not received", mainStreamLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue("Pushed data not received", pushDataLatch.await(5, TimeUnit.SECONDS));
+ if(validateHeaders)
+ Assert.assertTrue("Push syn headers not valid", pushSynHeadersValid.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testAssociatedResourceIsPushed() throws Exception
+ {
+ InetSocketAddress address = startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ String url = request.getRequestURI();
+ PrintWriter output = response.getWriter();
+ if (url.endsWith(".html"))
+ output.print("<html><head/><body>HELLO</body></html>");
+ else if (url.endsWith(".css"))
+ output.print("body { background: #FFF; }");
+ baseRequest.setHandled(true);
+ }
+ });
+ Session session1 = startClient(version(), address, null);
+
+ final CountDownLatch mainResourceLatch = new CountDownLatch(1);
+ Headers mainRequestHeaders = createHeadersWithoutReferrer(mainResource);
+
+ session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
+
+ final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
+ Headers associatedRequestHeaders = createHeaders(cssResource);
+ session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ associatedResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
+
+ // Create another client, and perform the same request for the main resource, we expect the css being pushed
+
+ final CountDownLatch mainStreamLatch = new CountDownLatch(2);
+ final CountDownLatch pushDataLatch = new CountDownLatch(1);
+ Session session2 = startClient(version(), address, new SessionFrameListener.Adapter()
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
+ {
+ Assert.assertTrue(stream.isUnidirectional());
+ return new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ pushDataLatch.countDown();
+ }
+ };
+ }
+ });
+ session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ mainStreamLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainStreamLatch.countDown();
+ }
+ });
+
+ Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testAssociatedResourceWithWrongContentTypeIsNotPushed() throws Exception
+ {
+ final String fakeResource = "/fake.png";
+ InetSocketAddress address = startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ String url = request.getRequestURI();
+ PrintWriter output = response.getWriter();
+ if (url.endsWith(".html"))
+ {
+ response.setContentType("text/html");
+ output.print("<html><head/><body>HELLO</body></html>");
+ }
+ else if (url.equals(fakeResource))
+ {
+ response.setContentType("text/html");
+ output.print("<html><head/><body>IMAGE</body></html>");
+ }
+ else if (url.endsWith(".css"))
+ {
+ response.setContentType("text/css");
+ output.print("body { background: #FFF; }");
+ }
+ baseRequest.setHandled(true);
+ }
+ });
+ Session session1 = startClient(version(), address, null);
+
+ final CountDownLatch mainResourceLatch = new CountDownLatch(1);
+ Headers mainRequestHeaders = createHeadersWithoutReferrer(mainResource);
+
+ session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
+
+ final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
+ String cssResource = "/stylesheet.css";
+ Headers associatedRequestHeaders = createHeaders(cssResource);
+ session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ associatedResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
+
+ final CountDownLatch fakeAssociatedResourceLatch = new CountDownLatch(1);
+ Headers fakeAssociatedRequestHeaders = createHeaders(fakeResource);
+ session1.syn(new SynInfo(fakeAssociatedRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ fakeAssociatedResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(fakeAssociatedResourceLatch.await(5, TimeUnit.SECONDS));
+
+ // Create another client, and perform the same request for the main resource,
+ // we expect the css being pushed but not the fake PNG
+
+ final CountDownLatch mainStreamLatch = new CountDownLatch(2);
+ final CountDownLatch pushDataLatch = new CountDownLatch(1);
+ Session session2 = startClient(version(), address, new SessionFrameListener.Adapter()
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
+ {
+ Assert.assertTrue(stream.isUnidirectional());
+ Assert.assertTrue(synInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version())).value().endsWith(".css"));
+ return new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ pushDataLatch.countDown();
+ }
+ };
+ }
+ });
+ session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ mainStreamLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainStreamLatch.countDown();
+ }
+ });
+
+ Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testNestedAssociatedResourceIsPushed() throws Exception
+ {
+ InetSocketAddress address = startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ String url = request.getRequestURI();
+ PrintWriter output = response.getWriter();
+ if (url.endsWith(".html"))
+ output.print("<html><head/><body>HELLO</body></html>");
+ else if (url.endsWith(".css"))
+ output.print("body { background: #FFF; }");
+ else if (url.endsWith(".gif"))
+ output.print("\u0000");
+ baseRequest.setHandled(true);
+ }
+ });
+ Session session1 = startClient(version(), address, null);
+
+ final CountDownLatch mainResourceLatch = new CountDownLatch(1);
+ Headers mainRequestHeaders = createHeadersWithoutReferrer(mainResource);
+
+ session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
+
+ final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
+ Headers associatedRequestHeaders = createHeaders(cssResource);
+ session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ associatedResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
+
+ final CountDownLatch nestedResourceLatch = new CountDownLatch(1);
+ String imageUrl = "/image.gif";
+ Headers nestedRequestHeaders = createHeaders(imageUrl, cssResource);
+
+ session1.syn(new SynInfo(nestedRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ nestedResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(nestedResourceLatch.await(5, TimeUnit.SECONDS));
+
+ // Create another client, and perform the same request for the main resource, we expect the css and the image being pushed
+
+ final CountDownLatch mainStreamLatch = new CountDownLatch(2);
+ final CountDownLatch pushDataLatch = new CountDownLatch(2);
+ Session session2 = startClient(version(), address, new SessionFrameListener.Adapter()
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
+ {
+ Assert.assertTrue(stream.isUnidirectional());
+ return new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ pushDataLatch.countDown();
+ }
+ };
+ }
+ });
+ session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ mainStreamLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainStreamLatch.countDown();
+ }
+ });
+
+ Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testMainResourceWithReferrerIsNotPushed() throws Exception
+ {
+ InetSocketAddress address = startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ String url = request.getRequestURI();
+ PrintWriter output = response.getWriter();
+ if (url.endsWith(".html"))
+ output.print("<html><head/><body>HELLO</body></html>");
+ baseRequest.setHandled(true);
+ }
+ });
+ Session session1 = startClient(version(), address, null);
+
+ final CountDownLatch mainResourceLatch = new CountDownLatch(1);
+ Headers mainRequestHeaders = createHeadersWithoutReferrer(mainResource);
+
+ session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
+
+ final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
+ String associatedResource = "/home.html";
+ Headers associatedRequestHeaders = createHeaders(associatedResource);
+
+ session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ associatedResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
+
+ // Create another client, and perform the same request for the main resource, we expect nothing being pushed
+
+ final CountDownLatch mainStreamLatch = new CountDownLatch(2);
+ final CountDownLatch pushLatch = new CountDownLatch(1);
+ Session session2 = startClient(version(), address, new SessionFrameListener.Adapter()
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
+ {
+ pushLatch.countDown();
+ return null;
+ }
+ });
+ session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ mainStreamLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainStreamLatch.countDown();
+ }
+ });
+
+ Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertFalse(pushLatch.await(1, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testRequestWithIfModifiedSinceHeaderPreventsPush() throws Exception
+ {
+ InetSocketAddress address = startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ String url = request.getRequestURI();
+ PrintWriter output = response.getWriter();
+ if (url.endsWith(".html"))
+ output.print("<html><head/><body>HELLO</body></html>");
+ else if (url.endsWith(".css"))
+ output.print("body { background: #FFF; }");
+ baseRequest.setHandled(true);
+ }
+ });
+ Session session1 = startClient(version(), address, null);
+
+ final CountDownLatch mainResourceLatch = new CountDownLatch(1);
+ Headers mainRequestHeaders = createHeaders(mainResource);
+ mainRequestHeaders.put("If-Modified-Since", "Tue, 27 Mar 2012 16:36:52 GMT");
+ session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
+
+ final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
+ Headers associatedRequestHeaders = createHeaders(cssResource);
+ session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ associatedResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
+
+ // Create another client, and perform the same request for the main resource, we expect the css NOT being pushed as the main request contains an
+ // if-modified-since header
+
+ final CountDownLatch mainStreamLatch = new CountDownLatch(2);
+ final CountDownLatch pushDataLatch = new CountDownLatch(1);
+ Session session2 = startClient(version(), address, new SessionFrameListener.Adapter()
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
+ {
+ Assert.assertTrue(stream.isUnidirectional());
+ return new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ pushDataLatch.countDown();
+ }
+ };
+ }
+ });
+ session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ mainStreamLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainStreamLatch.countDown();
+ }
+ });
+
+ Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertFalse("We don't expect data to be pushed as the main request contained an if-modified-since header",pushDataLatch.await(1, TimeUnit.SECONDS));
+ }
+
+ private void validateHeaders(Headers headers, CountDownLatch pushSynHeadersValid)
+ {
+ if (validateHeader(headers, HTTPSPDYHeader.STATUS.name(version()), "200")
+ && validateHeader(headers, HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1")
+ && validateUriHeader(headers))
+ pushSynHeadersValid.countDown();
+ }
+
+ private boolean validateHeader(Headers headers, String name, String expectedValue)
+ {
+ Headers.Header header = headers.get(name);
+ if (header != null && expectedValue.equals(header.value()))
+ return true;
+ System.out.println(name + " not valid! " + headers);
+ return false;
+ }
+
+ private boolean validateUriHeader(Headers headers)
+ {
+ Headers.Header uriHeader = headers.get(HTTPSPDYHeader.URI.name(version()));
+ if (uriHeader != null)
+ if (version() == SPDY.V2 && uriHeader.value().startsWith("http://"))
+ return true;
+ else if (version() == SPDY.V3 && uriHeader.value().startsWith("/")
+ && headers.get(HTTPSPDYHeader.HOST.name(version())) != null && headers.get(HTTPSPDYHeader.SCHEME.name(version())) != null)
+ return true;
+ System.out.println(HTTPSPDYHeader.URI.name(version()) + " not valid!");
+ return false;
+ }
+
+ private Headers createHeaders(String resource)
+ {
+ return createHeaders(resource, mainResource);
+ }
+
+ private Headers createHeaders(String resource, String referrer)
+ {
+ Headers associatedRequestHeaders = createHeadersWithoutReferrer(resource);
+ associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + referrer);
+ return associatedRequestHeaders;
+ }
+
+ private Headers createHeadersWithoutReferrer(String resource)
+ {
+ Headers associatedRequestHeaders = new Headers();
+ associatedRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ associatedRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), resource);
+ associatedRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ associatedRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ associatedRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ return associatedRequestHeaders;
+ }
+}
diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyV3Test.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyV3Test.java
new file mode 100644
index 0000000..2b637cb
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyV3Test.java
@@ -0,0 +1,26 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.http;
+
+import org.eclipse.jetty.spdy.api.SPDY;
+
+public class ReferrerPushStrategyV3Test extends ReferrerPushStrategyV2Test
+{
+ @Override
+ protected short version()
+ {
+ return SPDY.V3;
+ }
+}
diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/SSLExternalServerTest.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/SSLExternalServerTest.java
new file mode 100644
index 0000000..42bd5be
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/SSLExternalServerTest.java
@@ -0,0 +1,94 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.http;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jetty.spdy.SPDYClient;
+import org.eclipse.jetty.spdy.api.Headers;
+import org.eclipse.jetty.spdy.api.ReplyInfo;
+import org.eclipse.jetty.spdy.api.SPDY;
+import org.eclipse.jetty.spdy.api.Session;
+import org.eclipse.jetty.spdy.api.Stream;
+import org.eclipse.jetty.spdy.api.StreamFrameListener;
+import org.eclipse.jetty.spdy.api.SynInfo;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+
+public class SSLExternalServerTest extends AbstractHTTPSPDYTest
+{
+ @Override
+ protected SPDYClient.Factory newSPDYClientFactory(Executor threadPool)
+ {
+ SslContextFactory sslContextFactory = new SslContextFactory();
+ // Force TLSv1
+ sslContextFactory.setIncludeProtocols("TLSv1");
+ return new SPDYClient.Factory(threadPool, sslContextFactory);
+ }
+
+ @Test
+ public void testExternalServer() throws Exception
+ {
+ String host = "encrypted.google.com";
+ int port = 443;
+ InetSocketAddress address = new InetSocketAddress(host, port);
+
+ try
+ {
+ // Test whether there is connectivity to avoid fail the test when offline
+ Socket socket = new Socket();
+ socket.connect(address, 5000);
+ socket.close();
+ }
+ catch (IOException x)
+ {
+ Assume.assumeNoException(x);
+ }
+
+ final short version = SPDY.V2;
+ Session session = startClient(version, address, null);
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.SCHEME.name(version), "https");
+ headers.put(HTTPSPDYHeader.HOST.name(version), host + ":" + port);
+ headers.put(HTTPSPDYHeader.METHOD.name(version), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version), "/");
+ headers.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
+ final CountDownLatch latch = new CountDownLatch(1);
+ session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Headers headers = replyInfo.getHeaders();
+ Headers.Header versionHeader = headers.get(HTTPSPDYHeader.STATUS.name(version));
+ if (versionHeader != null)
+ {
+ Matcher matcher = Pattern.compile("(\\d{3}).*").matcher(versionHeader.value());
+ if (matcher.matches() && Integer.parseInt(matcher.group(1)) < 400)
+ latch.countDown();
+ }
+ }
+ });
+ Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+ }
+}
diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYv2Test.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYv2Test.java
new file mode 100644
index 0000000..5bab1f5
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYv2Test.java
@@ -0,0 +1,1272 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.http;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.continuation.Continuation;
+import org.eclipse.jetty.continuation.ContinuationSupport;
+import org.eclipse.jetty.io.ByteArrayBuffer;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.spdy.api.BytesDataInfo;
+import org.eclipse.jetty.spdy.api.DataInfo;
+import org.eclipse.jetty.spdy.api.Headers;
+import org.eclipse.jetty.spdy.api.ReplyInfo;
+import org.eclipse.jetty.spdy.api.Session;
+import org.eclipse.jetty.spdy.api.Stream;
+import org.eclipse.jetty.spdy.api.StreamFrameListener;
+import org.eclipse.jetty.spdy.api.StringDataInfo;
+import org.eclipse.jetty.spdy.api.SynInfo;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ServerHTTPSPDYv2Test extends AbstractHTTPSPDYTest
+{
+ @Test
+ public void testSimpleGET() throws Exception
+ {
+ final String path = "/foo";
+ final CountDownLatch handlerLatch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+ Assert.assertEquals("GET", httpRequest.getMethod());
+ Assert.assertEquals(path, target);
+ Assert.assertEquals(path, httpRequest.getRequestURI());
+ Assert.assertEquals("localhost:" + connector.getLocalPort(), httpRequest.getHeader("host"));
+ handlerLatch.countDown();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), path);
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertTrue(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ replyLatch.countDown();
+ }
+ });
+ Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testGETWithQueryString() throws Exception
+ {
+ final String path = "/foo";
+ final String query = "p=1";
+ final String uri = path + "?" + query;
+ final CountDownLatch handlerLatch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+ Assert.assertEquals("GET", httpRequest.getMethod());
+ Assert.assertEquals(path, target);
+ Assert.assertEquals(path, httpRequest.getRequestURI());
+ Assert.assertEquals(query, httpRequest.getQueryString());
+ handlerLatch.countDown();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), uri);
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertTrue(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ replyLatch.countDown();
+ }
+ });
+ Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testHEAD() throws Exception
+ {
+ final String path = "/foo";
+ final CountDownLatch handlerLatch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+ Assert.assertEquals("HEAD", httpRequest.getMethod());
+ Assert.assertEquals(path, target);
+ Assert.assertEquals(path, httpRequest.getRequestURI());
+ handlerLatch.countDown();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "HEAD");
+ headers.put(HTTPSPDYHeader.URI.name(version()), path);
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertTrue(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ replyLatch.countDown();
+ }
+ });
+ Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testPOSTWithParameters() throws Exception
+ {
+ final String path = "/foo";
+ final String data = "a=1&b=2";
+ final CountDownLatch handlerLatch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+ Assert.assertEquals("POST", httpRequest.getMethod());
+ Assert.assertEquals("1", httpRequest.getParameter("a"));
+ Assert.assertEquals("2", httpRequest.getParameter("b"));
+ Assert.assertNotNull(httpRequest.getRemoteHost());
+ Assert.assertNotNull(httpRequest.getRemotePort());
+ Assert.assertNotNull(httpRequest.getRemoteAddr());
+ Assert.assertNotNull(httpRequest.getLocalPort());
+ Assert.assertNotNull(httpRequest.getLocalName());
+ Assert.assertNotNull(httpRequest.getLocalAddr());
+ Assert.assertNotNull(httpRequest.getServerPort());
+ Assert.assertNotNull(httpRequest.getServerName());
+ handlerLatch.countDown();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "POST");
+ headers.put(HTTPSPDYHeader.URI.name(version()), path);
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ headers.put("content-type", "application/x-www-form-urlencoded");
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertTrue(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ replyLatch.countDown();
+ }
+ }).get(5, TimeUnit.SECONDS);
+ stream.data(new StringDataInfo(data, true));
+
+ Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testPOSTWithParametersInTwoFramesTwoReads() throws Exception
+ {
+ final String path = "/foo";
+ final String data1 = "a=1&";
+ final String data2 = "b=2";
+ final CountDownLatch handlerLatch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+ Assert.assertEquals("POST", httpRequest.getMethod());
+ Assert.assertEquals("1", httpRequest.getParameter("a"));
+ Assert.assertEquals("2", httpRequest.getParameter("b"));
+ handlerLatch.countDown();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "POST");
+ headers.put(HTTPSPDYHeader.URI.name(version()), path);
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ headers.put("content-type", "application/x-www-form-urlencoded");
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertTrue(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ replyLatch.countDown();
+ }
+ }).get(5, TimeUnit.SECONDS);
+ // Sleep between the data frames so that they will be read in 2 reads
+ stream.data(new StringDataInfo(data1, false));
+ Thread.sleep(1000);
+ stream.data(new StringDataInfo(data2, true));
+
+ Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testPOSTWithParametersInTwoFramesOneRead() throws Exception
+ {
+ final String path = "/foo";
+ final String data1 = "a=1&";
+ final String data2 = "b=2";
+ final CountDownLatch handlerLatch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+ Assert.assertEquals("POST", httpRequest.getMethod());
+ Assert.assertEquals("1", httpRequest.getParameter("a"));
+ Assert.assertEquals("2", httpRequest.getParameter("b"));
+ handlerLatch.countDown();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "POST");
+ headers.put(HTTPSPDYHeader.URI.name(version()), path);
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ headers.put("content-type", "application/x-www-form-urlencoded");
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertTrue(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.toString(), replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ replyLatch.countDown();
+ }
+ }).get(5, TimeUnit.SECONDS);
+ // Send the data frames consecutively, so the server reads both frames in one read
+ stream.data(new StringDataInfo(data1, false));
+ stream.data(new StringDataInfo(data2, true));
+
+ Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testGETWithSmallResponseContent() throws Exception
+ {
+ final String data = "0123456789ABCDEF";
+ final CountDownLatch handlerLatch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+ httpResponse.setStatus(HttpServletResponse.SC_OK);
+ ServletOutputStream output = httpResponse.getOutputStream();
+ output.write(data.getBytes("UTF-8"));
+ handlerLatch.countDown();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ final CountDownLatch dataLatch = new CountDownLatch(1);
+ session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ replyLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ Assert.assertTrue(dataInfo.isClose());
+ Assert.assertEquals(data, dataInfo.asString("UTF-8", true));
+ dataLatch.countDown();
+ }
+ });
+ Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testGETWithOneByteResponseContent() throws Exception
+ {
+ final char data = 'x';
+ final CountDownLatch handlerLatch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+ httpResponse.setStatus(HttpServletResponse.SC_OK);
+ ServletOutputStream output = httpResponse.getOutputStream();
+ output.write(data);
+ handlerLatch.countDown();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ final CountDownLatch dataLatch = new CountDownLatch(1);
+ session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ replyLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ Assert.assertTrue(dataInfo.isClose());
+ byte[] bytes = dataInfo.asBytes(true);
+ Assert.assertEquals(1, bytes.length);
+ Assert.assertEquals(data, bytes[0]);
+ dataLatch.countDown();
+ }
+ });
+ Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testGETWithSmallResponseContentInTwoChunks() throws Exception
+ {
+ final String data1 = "0123456789ABCDEF";
+ final String data2 = "FEDCBA9876543210";
+ final CountDownLatch handlerLatch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+ httpResponse.setStatus(HttpServletResponse.SC_OK);
+ ServletOutputStream output = httpResponse.getOutputStream();
+ output.write(data1.getBytes("UTF-8"));
+ output.flush();
+ output.write(data2.getBytes("UTF-8"));
+ handlerLatch.countDown();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ final CountDownLatch dataLatch = new CountDownLatch(2);
+ session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
+ {
+ private final AtomicInteger replyFrames = new AtomicInteger();
+ private final AtomicInteger dataFrames = new AtomicInteger();
+
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertEquals(1, replyFrames.incrementAndGet());
+ Assert.assertFalse(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ replyLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ int data = dataFrames.incrementAndGet();
+ Assert.assertTrue(data >= 1 && data <= 2);
+ if (data == 1)
+ Assert.assertEquals(data1, dataInfo.asString("UTF8", true));
+ else
+ Assert.assertEquals(data2, dataInfo.asString("UTF8", true));
+ dataLatch.countDown();
+ }
+ });
+ Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testGETWithBigResponseContentInOneWrite() throws Exception
+ {
+ final byte[] data = new byte[128 * 1024];
+ Arrays.fill(data, (byte)'x');
+ final CountDownLatch handlerLatch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+ httpResponse.setStatus(HttpServletResponse.SC_OK);
+ ServletOutputStream output = httpResponse.getOutputStream();
+ output.write(data);
+ handlerLatch.countDown();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ final CountDownLatch dataLatch = new CountDownLatch(1);
+ session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
+ {
+ private final AtomicInteger contentBytes = new AtomicInteger();
+
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ replyLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ contentBytes.addAndGet(dataInfo.asByteBuffer(true).remaining());
+ if (dataInfo.isClose())
+ {
+ Assert.assertEquals(data.length, contentBytes.get());
+ dataLatch.countDown();
+ }
+ }
+ });
+ Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testGETWithBigResponseContentInTwoWrites() throws Exception
+ {
+ final byte[] data = new byte[128 * 1024];
+ Arrays.fill(data, (byte)'y');
+ final CountDownLatch handlerLatch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+ httpResponse.setStatus(HttpServletResponse.SC_OK);
+ ServletOutputStream output = httpResponse.getOutputStream();
+ output.write(data);
+ output.write(data);
+ handlerLatch.countDown();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ final CountDownLatch dataLatch = new CountDownLatch(1);
+ session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
+ {
+ private final AtomicInteger contentBytes = new AtomicInteger();
+
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ replyLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ contentBytes.addAndGet(dataInfo.asByteBuffer(true).remaining());
+ if (dataInfo.isClose())
+ {
+ Assert.assertEquals(2 * data.length, contentBytes.get());
+ dataLatch.countDown();
+ }
+ }
+ });
+ Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testGETWithOutputStreamFlushedAndClosed() throws Exception
+ {
+ final String data = "0123456789ABCDEF";
+ final CountDownLatch handlerLatch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+ httpResponse.setStatus(HttpServletResponse.SC_OK);
+ ServletOutputStream output = httpResponse.getOutputStream();
+ output.write(data.getBytes("UTF-8"));
+ output.flush();
+ output.close();
+ handlerLatch.countDown();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ final CountDownLatch dataLatch = new CountDownLatch(1);
+ session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
+ {
+ private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ replyLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ ByteBuffer byteBuffer = dataInfo.asByteBuffer(true);
+ while (byteBuffer.hasRemaining())
+ buffer.write(byteBuffer.get());
+ if (dataInfo.isClose())
+ {
+ Assert.assertEquals(data, new String(buffer.toByteArray(), Charset.forName("UTF-8")));
+ dataLatch.countDown();
+ }
+ }
+ });
+ Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testGETWithResponseResetBuffer() throws Exception
+ {
+ final String data1 = "0123456789ABCDEF";
+ final String data2 = "FEDCBA9876543210";
+ final CountDownLatch handlerLatch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+ httpResponse.setStatus(HttpServletResponse.SC_OK);
+ ServletOutputStream output = httpResponse.getOutputStream();
+ // Write some
+ output.write(data1.getBytes("UTF-8"));
+ // But then change your mind and reset the buffer
+ httpResponse.resetBuffer();
+ output.write(data2.getBytes("UTF-8"));
+ handlerLatch.countDown();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ final CountDownLatch dataLatch = new CountDownLatch(1);
+ session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
+ {
+ private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ replyLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ ByteBuffer byteBuffer = dataInfo.asByteBuffer(true);
+ while (byteBuffer.hasRemaining())
+ buffer.write(byteBuffer.get());
+ if (dataInfo.isClose())
+ {
+ Assert.assertEquals(data2, new String(buffer.toByteArray(), Charset.forName("UTF-8")));
+ dataLatch.countDown();
+ }
+ }
+ });
+ Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testGETWithRedirect() throws Exception
+ {
+ final String suffix = "/redirect";
+ final CountDownLatch handlerLatch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+ String location = httpResponse.encodeRedirectURL(String.format("%s://%s:%d%s",
+ request.getScheme(), request.getLocalAddr(), request.getLocalPort(), target + suffix));
+ httpResponse.sendRedirect(location);
+ handlerLatch.countDown();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
+ {
+ private final AtomicInteger replies = new AtomicInteger();
+
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertEquals(1, replies.incrementAndGet());
+ Assert.assertTrue(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("302"));
+ Assert.assertTrue(replyHeaders.get("location").value().endsWith(suffix));
+ replyLatch.countDown();
+ }
+ });
+ Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testGETWithSendError() throws Exception
+ {
+ final CountDownLatch handlerLatch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+ httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
+ handlerLatch.countDown();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ final CountDownLatch dataLatch = new CountDownLatch(1);
+ session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
+ {
+ private final AtomicInteger replies = new AtomicInteger();
+
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertEquals(1, replies.incrementAndGet());
+ Assert.assertFalse(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("404"));
+ replyLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ if (dataInfo.isClose())
+ dataLatch.countDown();
+ }
+ });
+ Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testGETWithException() throws Exception
+ {
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ throw new NullPointerException("thrown_explicitly_by_the_test");
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
+ {
+ private final AtomicInteger replies = new AtomicInteger();
+
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertEquals(1, replies.incrementAndGet());
+ Assert.assertTrue(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("500"));
+ replyLatch.countDown();
+ }
+ });
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testGETWithSmallResponseChunked() throws Exception
+ {
+ final String pangram1 = "the quick brown fox jumps over the lazy dog";
+ final String pangram2 = "qualche vago ione tipo zolfo, bromo, sodio";
+ final CountDownLatch handlerLatch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+ httpResponse.setHeader("Transfer-Encoding", "chunked");
+ ServletOutputStream output = httpResponse.getOutputStream();
+ output.write(pangram1.getBytes("UTF-8"));
+ httpResponse.setHeader("EXTRA", "X");
+ output.flush();
+ output.write(pangram2.getBytes("UTF-8"));
+ handlerLatch.countDown();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ final CountDownLatch dataLatch = new CountDownLatch(2);
+ session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
+ {
+ private final AtomicInteger replyFrames = new AtomicInteger();
+ private final AtomicInteger dataFrames = new AtomicInteger();
+
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertEquals(1, replyFrames.incrementAndGet());
+ Assert.assertFalse(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ Assert.assertTrue(replyHeaders.get("extra").value().contains("X"));
+ replyLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ int count = dataFrames.incrementAndGet();
+ if (count == 1)
+ {
+ Assert.assertFalse(dataInfo.isClose());
+ Assert.assertEquals(pangram1, dataInfo.asString("UTF-8", true));
+ }
+ else if (count == 2)
+ {
+ Assert.assertTrue(dataInfo.isClose());
+ Assert.assertEquals(pangram2, dataInfo.asString("UTF-8", true));
+ }
+ dataLatch.countDown();
+ }
+ });
+ Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testGETWithMediumContentAsInputStreamByPassed() throws Exception
+ {
+ byte[] data = new byte[2048];
+ testGETWithContentByPassed(new ByteArrayInputStream(data), data.length);
+ }
+
+ @Test
+ public void testGETWithBigContentAsInputStreamByPassed() throws Exception
+ {
+ byte[] data = new byte[128 * 1024];
+ testGETWithContentByPassed(new ByteArrayInputStream(data), data.length);
+ }
+
+ @Test
+ public void testGETWithMediumContentAsBufferByPassed() throws Exception
+ {
+ byte[] data = new byte[2048];
+ testGETWithContentByPassed(new ByteArrayBuffer(data), data.length);
+ }
+
+ private void testGETWithContentByPassed(final Object content, final int length) throws Exception
+ {
+ final CountDownLatch handlerLatch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+ // We use this trick that's present in Jetty code: if we add a request attribute
+ // called "org.eclipse.jetty.server.sendContent", then it will trigger the
+ // content bypass that we want to test
+ request.setAttribute("org.eclipse.jetty.server.sendContent", content);
+ handlerLatch.countDown();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ final CountDownLatch dataLatch = new CountDownLatch(1);
+ session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
+ {
+ private final AtomicInteger replyFrames = new AtomicInteger();
+ private final AtomicInteger contentLength = new AtomicInteger();
+
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertEquals(1, replyFrames.incrementAndGet());
+ Assert.assertFalse(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ replyLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ contentLength.addAndGet(dataInfo.asBytes(true).length);
+ if (dataInfo.isClose())
+ {
+ Assert.assertEquals(length, contentLength.get());
+ dataLatch.countDown();
+ }
+ }
+ });
+ Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testGETWithMultipleMediumContentByPassed() throws Exception
+ {
+ final byte[] data = new byte[2048];
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ // The sequence of write/flush/write/write below triggers a condition where
+ // HttpGenerator._bypass is set to true on the second write(), and the
+ // third write causes an infinite spin loop on the third write().
+ request.setHandled(true);
+ OutputStream output = httpResponse.getOutputStream();
+ output.write(data);
+ output.flush();
+ output.write(data);
+ output.write(data);
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ final CountDownLatch dataLatch = new CountDownLatch(1);
+ final AtomicInteger contentLength = new AtomicInteger();
+ session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ replyLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.available());
+ contentLength.addAndGet(dataInfo.length());
+ if (dataInfo.isClose())
+ dataLatch.countDown();
+ }
+ });
+ Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertEquals(3 * data.length, contentLength.get());
+ }
+
+ @Test
+ public void testPOSTThenSuspendRequestThenReadOneChunkThenComplete() throws Exception
+ {
+ final byte[] data = new byte[2000];
+ final CountDownLatch latch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, final Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+
+ final Continuation continuation = ContinuationSupport.getContinuation(request);
+ continuation.suspend();
+
+ new Thread()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ InputStream input = request.getInputStream();
+ byte[] buffer = new byte[512];
+ int read = 0;
+ while (read < data.length)
+ read += input.read(buffer);
+ continuation.complete();
+ latch.countDown();
+ }
+ catch (IOException x)
+ {
+ x.printStackTrace();
+ }
+ }
+ }.start();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "POST");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ replyLatch.countDown();
+ }
+ }).get(5, TimeUnit.SECONDS);
+ stream.data(new BytesDataInfo(data, true));
+
+ Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testPOSTThenSuspendRequestThenReadTwoChunksThenComplete() throws Exception
+ {
+ final byte[] data = new byte[2000];
+ final CountDownLatch latch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, final Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+
+ final Continuation continuation = ContinuationSupport.getContinuation(request);
+ continuation.suspend();
+
+ new Thread()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ InputStream input = request.getInputStream();
+ byte[] buffer = new byte[512];
+ int read = 0;
+ while (read < 2 * data.length)
+ read += input.read(buffer);
+ continuation.complete();
+ latch.countDown();
+ }
+ catch (IOException x)
+ {
+ x.printStackTrace();
+ }
+ }
+ }.start();
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "POST");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch replyLatch = new CountDownLatch(1);
+ Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ replyLatch.countDown();
+ }
+ }).get(5, TimeUnit.SECONDS);
+ stream.data(new BytesDataInfo(data, false));
+ stream.data(new BytesDataInfo(data, true));
+
+ Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testPOSTThenSuspendRequestThenResumeThenRespond() throws Exception
+ {
+ final byte[] data = new byte[1000];
+ final CountDownLatch latch = new CountDownLatch(1);
+ Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, final Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+ request.setHandled(true);
+
+ final Continuation continuation = ContinuationSupport.getContinuation(request);
+
+ if (continuation.isInitial())
+ {
+ InputStream input = request.getInputStream();
+ byte[] buffer = new byte[256];
+ int read = 0;
+ while (read < data.length)
+ read += input.read(buffer);
+ continuation.suspend();
+ new Thread()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ TimeUnit.SECONDS.sleep(1);
+ continuation.resume();
+ latch.countDown();
+ }
+ catch (InterruptedException x)
+ {
+ x.printStackTrace();
+ }
+ }
+ }.start();
+ }
+ else
+ {
+ OutputStream output = httpResponse.getOutputStream();
+ output.write(data);
+ }
+ }
+ }), null);
+
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "POST");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ final CountDownLatch responseLatch = new CountDownLatch(2);
+ Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Headers replyHeaders = replyInfo.getHeaders();
+ Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
+ responseLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ if (dataInfo.isClose())
+ responseLatch.countDown();
+ }
+ }).get(5, TimeUnit.SECONDS);
+ stream.data(new BytesDataInfo(data, true));
+
+ Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
+ }
+}
diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYv3Test.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYv3Test.java
new file mode 100644
index 0000000..e6c4de2
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYv3Test.java
@@ -0,0 +1,26 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.http;
+
+import org.eclipse.jetty.spdy.api.SPDY;
+
+public class ServerHTTPSPDYv3Test extends ServerHTTPSPDYv2Test
+{
+ @Override
+ protected short version()
+ {
+ return SPDY.V3;
+ }
+}
diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPSPDYv2Test.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPSPDYv2Test.java
new file mode 100644
index 0000000..6c2c89b
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPSPDYv2Test.java
@@ -0,0 +1,764 @@
+//========================================================================
+//Copyright 2011-2012 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.spdy.proxy;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.spdy.SPDYClient;
+import org.eclipse.jetty.spdy.SPDYServerConnector;
+import org.eclipse.jetty.spdy.ServerSPDYAsyncConnectionFactory;
+import org.eclipse.jetty.spdy.api.BytesDataInfo;
+import org.eclipse.jetty.spdy.api.DataInfo;
+import org.eclipse.jetty.spdy.api.GoAwayInfo;
+import org.eclipse.jetty.spdy.api.Handler;
+import org.eclipse.jetty.spdy.api.Headers;
+import org.eclipse.jetty.spdy.api.PingInfo;
+import org.eclipse.jetty.spdy.api.ReplyInfo;
+import org.eclipse.jetty.spdy.api.RstInfo;
+import org.eclipse.jetty.spdy.api.SPDY;
+import org.eclipse.jetty.spdy.api.Session;
+import org.eclipse.jetty.spdy.api.SessionFrameListener;
+import org.eclipse.jetty.spdy.api.Stream;
+import org.eclipse.jetty.spdy.api.StreamFrameListener;
+import org.eclipse.jetty.spdy.api.StreamStatus;
+import org.eclipse.jetty.spdy.api.SynInfo;
+import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
+import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestWatchman;
+import org.junit.runners.model.FrameworkMethod;
+
+public class ProxyHTTPSPDYv2Test
+{
+ @Rule
+ public final TestWatchman testName = new TestWatchman()
+ {
+ @Override
+ public void starting(FrameworkMethod method)
+ {
+ super.starting(method);
+ System.err.printf("Running %s.%s()%n",
+ method.getMethod().getDeclaringClass().getName(),
+ method.getName());
+ }
+ };
+
+ private SPDYClient.Factory factory;
+ private Server server;
+ private Server proxy;
+ private SPDYServerConnector proxyConnector;
+
+ protected short version()
+ {
+ return SPDY.V2;
+ }
+
+ protected InetSocketAddress startServer(ServerSessionFrameListener listener) throws Exception
+ {
+ server = new Server();
+ SPDYServerConnector serverConnector = new SPDYServerConnector(listener);
+ serverConnector.setDefaultAsyncConnectionFactory(new ServerSPDYAsyncConnectionFactory(version(), serverConnector.getByteBufferPool(), serverConnector.getExecutor(), serverConnector.getScheduler(), listener));
+ serverConnector.setPort(0);
+ server.addConnector(serverConnector);
+ server.start();
+ return new InetSocketAddress("localhost", serverConnector.getLocalPort());
+ }
+
+ protected InetSocketAddress startProxy(InetSocketAddress address) throws Exception
+ {
+ proxy = new Server();
+ ProxyEngineSelector proxyEngineSelector = new ProxyEngineSelector();
+ SPDYProxyEngine spdyProxyEngine = new SPDYProxyEngine(factory);
+ proxyEngineSelector.putProxyEngine("spdy/" + version(), spdyProxyEngine);
+ proxyEngineSelector.putProxyServerInfo("localhost", new ProxyEngineSelector.ProxyServerInfo("spdy/" + version(), address.getHostName(), address.getPort()));
+ proxyConnector = new HTTPSPDYProxyConnector(proxyEngineSelector);
+ proxyConnector.setPort(0);
+ proxy.addConnector(proxyConnector);
+ proxy.start();
+ return new InetSocketAddress("localhost", proxyConnector.getLocalPort());
+ }
+
+ @Before
+ public void init() throws Exception
+ {
+ factory = new SPDYClient.Factory();
+ factory.start();
+ }
+
+ @After
+ public void destroy() throws Exception
+ {
+ if (server != null)
+ {
+ server.stop();
+ server.join();
+ }
+ if (proxy != null)
+ {
+ proxy.stop();
+ proxy.join();
+ }
+ factory.stop();
+ }
+
+ @Test
+ public void testClosingClientDoesNotCloseServer() throws Exception
+ {
+ final CountDownLatch closeLatch = new CountDownLatch(1);
+ InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
+ {
+ Headers responseHeaders = new Headers();
+ responseHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ responseHeaders.put(HTTPSPDYHeader.STATUS.name(version()), "200 OK");
+ stream.reply(new ReplyInfo(responseHeaders, true));
+ return null;
+ }
+
+ @Override
+ public void onGoAway(Session session, GoAwayInfo goAwayInfo)
+ {
+ closeLatch.countDown();
+ }
+ }));
+
+ Socket client = new Socket();
+ client.connect(proxyAddress);
+ OutputStream output = client.getOutputStream();
+
+ String request = "" +
+ "GET / HTTP/1.1\r\n" +
+ "Host: localhost:" + proxyAddress.getPort() + "\r\n" +
+ "\r\n";
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ InputStream input = client.getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
+ String line = reader.readLine();
+ Assert.assertTrue(line.contains(" 200"));
+ while (line.length() > 0)
+ line = reader.readLine();
+ Assert.assertFalse(reader.ready());
+
+ client.close();
+
+ // Must not close, other clients may still be connected
+ Assert.assertFalse(closeLatch.await(1, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testGETThenNoContentFromTwoClients() throws Exception
+ {
+ InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
+ {
+ Assert.assertTrue(synInfo.isClose());
+ Headers requestHeaders = synInfo.getHeaders();
+ Assert.assertNotNull(requestHeaders.get("via"));
+
+ Headers responseHeaders = new Headers();
+ responseHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ responseHeaders.put(HTTPSPDYHeader.STATUS.name(version()), "200 OK");
+ ReplyInfo replyInfo = new ReplyInfo(responseHeaders, true);
+ stream.reply(replyInfo);
+ return null;
+ }
+ }));
+
+ Socket client1 = new Socket();
+ client1.connect(proxyAddress);
+ OutputStream output1 = client1.getOutputStream();
+
+ String request = "" +
+ "GET / HTTP/1.1\r\n" +
+ "Host: localhost:" + proxyAddress.getPort() + "\r\n" +
+ "\r\n";
+ output1.write(request.getBytes("UTF-8"));
+ output1.flush();
+
+ InputStream input1 = client1.getInputStream();
+ BufferedReader reader1 = new BufferedReader(new InputStreamReader(input1, "UTF-8"));
+ String line = reader1.readLine();
+ Assert.assertTrue(line.contains(" 200"));
+ while (line.length() > 0)
+ line = reader1.readLine();
+ Assert.assertFalse(reader1.ready());
+
+ // Perform another request with another client
+ Socket client2 = new Socket();
+ client2.connect(proxyAddress);
+ OutputStream output2 = client2.getOutputStream();
+
+ output2.write(request.getBytes("UTF-8"));
+ output2.flush();
+
+ InputStream input2 = client2.getInputStream();
+ BufferedReader reader2 = new BufferedReader(new InputStreamReader(input2, "UTF-8"));
+ line = reader2.readLine();
+ Assert.assertTrue(line.contains(" 200"));
+ while (line.length() > 0)
+ line = reader2.readLine();
+ Assert.assertFalse(reader2.ready());
+
+ client1.close();
+ client2.close();
+ }
+
+ @Test
+ public void testGETThenSmallResponseContent() throws Exception
+ {
+ final byte[] data = "0123456789ABCDEF".getBytes("UTF-8");
+ InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
+ {
+ Assert.assertTrue(synInfo.isClose());
+ Headers requestHeaders = synInfo.getHeaders();
+ Assert.assertNotNull(requestHeaders.get("via"));
+
+ Headers responseHeaders = new Headers();
+ responseHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ responseHeaders.put(HTTPSPDYHeader.STATUS.name(version()), "200 OK");
+ ReplyInfo replyInfo = new ReplyInfo(responseHeaders, false);
+ stream.reply(replyInfo);
+ stream.data(new BytesDataInfo(data, true));
+
+ return null;
+ }
+ }));
+
+ Socket client = new Socket();
+ client.connect(proxyAddress);
+ OutputStream output = client.getOutputStream();
+
+ String request = "" +
+ "GET / HTTP/1.1\r\n" +
+ "Host: localhost:" + proxyAddress.getPort() + "\r\n" +
+ "\r\n";
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ InputStream input = client.getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
+ String line = reader.readLine();
+ Assert.assertTrue(line.contains(" 200"));
+ while (line.length() > 0)
+ line = reader.readLine();
+ for (byte datum : data)
+ Assert.assertEquals(datum, reader.read());
+ Assert.assertFalse(reader.ready());
+
+ // Perform another request so that we are sure we reset the states of parsers and generators
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ line = reader.readLine();
+ Assert.assertTrue(line.contains(" 200"));
+ while (line.length() > 0)
+ line = reader.readLine();
+ for (byte datum : data)
+ Assert.assertEquals(datum, reader.read());
+ Assert.assertFalse(reader.ready());
+
+ client.close();
+ }
+
+ @Test
+ public void testPOSTWithSmallRequestContentThenRedirect() throws Exception
+ {
+ final byte[] data = "0123456789ABCDEF".getBytes("UTF-8");
+ InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
+ {
+ return new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ {
+ Headers headers = new Headers();
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.STATUS.name(version()), "303 See Other");
+ stream.reply(new ReplyInfo(headers, true));
+ }
+ }
+ };
+ }
+ }));
+
+ Socket client = new Socket();
+ client.connect(proxyAddress);
+ OutputStream output = client.getOutputStream();
+
+ String request = "" +
+ "POST / HTTP/1.1\r\n" +
+ "Host: localhost:" + proxyAddress.getPort() + "\r\n" +
+ "Content-Length: " + data.length + "\r\n" +
+ "Content-Type: application/octet-stream\r\n" +
+ "\r\n";
+ output.write(request.getBytes("UTF-8"));
+ output.write(data);
+ output.flush();
+
+ InputStream input = client.getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
+ String line = reader.readLine();
+ Assert.assertTrue(line.contains(" 303"));
+ while (line.length() > 0)
+ line = reader.readLine();
+ Assert.assertFalse(reader.ready());
+
+ // Perform another request so that we are sure we reset the states of parsers and generators
+ output.write(request.getBytes("UTF-8"));
+ output.write(data);
+ output.flush();
+
+ line = reader.readLine();
+ Assert.assertTrue(line.contains(" 303"));
+ while (line.length() > 0)
+ line = reader.readLine();
+ Assert.assertFalse(reader.ready());
+
+ client.close();
+ }
+
+ @Test
+ public void testPOSTWithSmallRequestContentThenSmallResponseContent() throws Exception
+ {
+ final byte[] data = "0123456789ABCDEF".getBytes("UTF-8");
+ InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
+ {
+ return new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ {
+ Headers responseHeaders = new Headers();
+ responseHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ responseHeaders.put(HTTPSPDYHeader.STATUS.name(version()), "200 OK");
+ ReplyInfo replyInfo = new ReplyInfo(responseHeaders, false);
+ stream.reply(replyInfo);
+ stream.data(new BytesDataInfo(data, true));
+ }
+ }
+ };
+ }
+ }));
+
+ Socket client = new Socket();
+ client.connect(proxyAddress);
+ OutputStream output = client.getOutputStream();
+
+ String request = "" +
+ "POST / HTTP/1.1\r\n" +
<