Author: markt
Date: Wed Oct 14 14:29:31 2015
New Revision: 1708605
URL: http://svn.apache.org/viewvc?rev=1708605&view=rev
Log:
Implement a very basic (you can only specify the path at the moment) server
push mechanism.
Add an example to the examples web app that shows how to use it.
Added:
tomcat/trunk/webapps/examples/WEB-INF/classes/http2/
tomcat/trunk/webapps/examples/WEB-INF/classes/http2/SimpleImagePush.java
(with props)
Modified:
tomcat/trunk/java/javax/servlet/http/PushBuilder.java
tomcat/trunk/java/org/apache/catalina/core/ApplicationPushBuilder.java
tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties
tomcat/trunk/java/org/apache/coyote/ActionCode.java
tomcat/trunk/java/org/apache/coyote/ajp/AjpProcessor.java
tomcat/trunk/java/org/apache/coyote/ajp/LocalStrings.properties
tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java
tomcat/trunk/java/org/apache/coyote/http11/LocalStrings.properties
tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java
tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties
tomcat/trunk/java/org/apache/coyote/http2/Stream.java
tomcat/trunk/java/org/apache/coyote/http2/StreamProcessor.java
tomcat/trunk/webapps/examples/WEB-INF/web.xml
tomcat/trunk/webapps/examples/servlets/index.html
Modified: tomcat/trunk/java/javax/servlet/http/PushBuilder.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/javax/servlet/http/PushBuilder.java?rev=1708605&r1=1708604&r2=1708605&view=diff
==============================================================================
--- tomcat/trunk/java/javax/servlet/http/PushBuilder.java (original)
+++ tomcat/trunk/java/javax/servlet/http/PushBuilder.java Wed Oct 14 14:29:31
2015
@@ -18,9 +18,32 @@ package javax.servlet.http;
/**
* Builds a push request based on the {@link HttpServletRequest} from which
this
- * builder was obtained.
+ * builder was obtained. The push request will be constructed on the following
+ * basis:
+ * <ul>
+ * <li>The request method is set to <code>GET</code></li>
+ * <li>The path will not be set. This must be set explicitly via a call to
+ * {@link #setPath(String)}</li>
+ * </ul>
*
* @since Servlet 4.0
*/
public interface PushBuilder {
+
+ /**
+ * Sets the URI path to be used for the push request. This must be called
+ * before every call to {@link #push()}. If the path includes a query
+ * string, the query string will be appended to the existing query string
+ * (if any) and no de-duplication will occur.
+ *
+ * @param path Paths beginning with '/' are treated as absolute paths. All
+ * other paths are treated as relative to the context path of
+ * the request used to create this builder instance. The path
+ * may include a query string.
+ *
+ * @return This builder instance
+ */
+ PushBuilder setPath(String path);
+
+ void push();
}
Modified: tomcat/trunk/java/org/apache/catalina/core/ApplicationPushBuilder.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/ApplicationPushBuilder.java?rev=1708605&r1=1708604&r2=1708605&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/ApplicationPushBuilder.java
(original)
+++ tomcat/trunk/java/org/apache/catalina/core/ApplicationPushBuilder.java Wed
Oct 14 14:29:31 2015
@@ -16,14 +16,73 @@
*/
package org.apache.catalina.core;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestWrapper;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.PushBuilder;
+import org.apache.catalina.connector.Request;
+import org.apache.coyote.ActionCode;
+import org.apache.tomcat.util.res.StringManager;
+
public class ApplicationPushBuilder implements PushBuilder {
+ private static final StringManager sm =
StringManager.getManager(ApplicationPushBuilder.class);
+
private final HttpServletRequest baseRequest;
+ private final org.apache.coyote.Request coyoteRequest;
+
+ private String path;
public ApplicationPushBuilder(HttpServletRequest request) {
baseRequest = request;
+ // Need a reference to the CoyoteRequest in order to process the push
+ ServletRequest current = request;
+ while (current instanceof ServletRequestWrapper) {
+ current = ((ServletRequestWrapper) current).getRequest();
+ }
+ if (current instanceof Request) {
+ coyoteRequest = ((Request) current).getCoyoteRequest();
+ } else {
+ throw new UnsupportedOperationException(sm.getString(
+ "applicationPushBuilder.noCoyoteRequest",
current.getClass().getName()));
+ }
+ }
+
+
+ @Override
+ public PushBuilder setPath(String path) {
+ if (path.startsWith("/")) {
+ this.path = path;
+ } else {
+ String contextPath = baseRequest.getContextPath();
+ int len = contextPath.length() + path.length() + 1;
+ StringBuilder sb = new StringBuilder(len);
+ sb.append(contextPath);
+ sb.append('/');
+ sb.append(path);
+ this.path = sb.toString();
+ }
+ return this;
+ }
+
+
+ @Override
+ public void push() {
+ org.apache.coyote.Request pushTarget = new org.apache.coyote.Request();
+
+ pushTarget.method().setString("GET");
+ // The next three are implied by the Javadoc getPath()
+ pushTarget.serverName().setString(baseRequest.getServerName());
+ pushTarget.setServerPort(baseRequest.getServerPort());
+ pushTarget.scheme().setString(baseRequest.getScheme());
+
+ pushTarget.requestURI().setString(path);
+ pushTarget.decodedURI().setString(path);
+
+ // TODO Copy across / set other required attributes
+
+ coyoteRequest.action(ActionCode.PUSH_REQUEST, pushTarget);
+ pushTarget = null;
}
}
Modified: tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties?rev=1708605&r1=1708604&r2=1708605&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties
(original)
+++ tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties Wed Oct
14 14:29:31 2015
@@ -48,6 +48,9 @@ applicationFilterConfig.jmxUnregisterFai
applicationFilterConfig.release=Failed to destroy the filter named [{0}] of
type [{1}]
applicationFilterRegistration.nullInitParam=Unable to set initialisation
parameter for filter due to null name and/or value. Name [{0}], Value [{1}]
applicationFilterRegistration.nullInitParams=Unable to set initialisation
parameters for filter due to null name and/or value. Name [{0}], Value [{1}]
+
+applicationPushBuilder.noCoyoteRequest=Unable to find the underlying Coyote
request object (which is required to create a push request) from the request of
type [{0}]
+
applicationServletRegistration.setServletSecurity.iae=Null constraint
specified for servlet [{0}] deployed to context with name [{1}]
applicationServletRegistration.setServletSecurity.ise=Security constraints
can't be added to servlet [{0}] deployed to context with name [{1}] as the
context has already been initialised
applicationSessionCookieConfig.ise=Property {0} cannot be added to
SessionCookieConfig for context {1} as the context has been initialised
Modified: tomcat/trunk/java/org/apache/coyote/ActionCode.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/ActionCode.java?rev=1708605&r1=1708604&r2=1708605&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/ActionCode.java (original)
+++ tomcat/trunk/java/org/apache/coyote/ActionCode.java Wed Oct 14 14:29:31 2015
@@ -236,5 +236,10 @@ public enum ActionCode {
* Trigger end of request processing (remaining input swallowed, write any
* remaining parts of the response etc.).
*/
- END_REQUEST
+ END_REQUEST,
+
+ /**
+ * Push a request on behalf of the client of the current request.
+ */
+ PUSH_REQUEST
}
Modified: tomcat/trunk/java/org/apache/coyote/ajp/AjpProcessor.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/ajp/AjpProcessor.java?rev=1708605&r1=1708604&r2=1708605&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/ajp/AjpProcessor.java (original)
+++ tomcat/trunk/java/org/apache/coyote/ajp/AjpProcessor.java Wed Oct 14
14:29:31 2015
@@ -594,6 +594,13 @@ public class AjpProcessor extends Abstra
// NO-OP for AJP
break;
}
+
+ // Servlet 4.0 Push requests
+ case PUSH_REQUEST: {
+ // HTTP2 connections only. Unsupported for AJP.
+ throw new UnsupportedOperationException(
+ sm.getString("ajpprocessor.pushrequest.notsupported"));
+ }
}
}
Modified: tomcat/trunk/java/org/apache/coyote/ajp/LocalStrings.properties
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/ajp/LocalStrings.properties?rev=1708605&r1=1708604&r2=1708605&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/ajp/LocalStrings.properties (original)
+++ tomcat/trunk/java/org/apache/coyote/ajp/LocalStrings.properties Wed Oct 14
14:29:31 2015
@@ -27,6 +27,7 @@ ajpprocessor.request.prepare=Error prepa
ajpprocessor.request.process=Error processing request
ajpprocessor.certs.fail=Certificate conversion failed
ajpprocessor.httpupgrade.notsupported=HTTP upgrade is not supported by the AJP
protocol
+ajpprocessor.pushrequest.notsupported=Server push requests are not supported
by the AJP protocol
ajpmessage.null=Cannot append null value
ajpmessage.overflow=Overflow error for buffer adding {0} bytes at position {1}
Modified: tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java?rev=1708605&r1=1708604&r2=1708605&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java Wed Oct 14
14:29:31 2015
@@ -904,6 +904,13 @@ public class Http11Processor extends Abs
endRequest();
break;
}
+
+ // Servlet 4.0 Push requests
+ case PUSH_REQUEST: {
+ // HTTP2 connections only. Unsupported for AJP.
+ throw new UnsupportedOperationException(
+ sm.getString("http11processor.pushrequest.notsupported"));
+ }
}
}
Modified: tomcat/trunk/java/org/apache/coyote/http11/LocalStrings.properties
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/LocalStrings.properties?rev=1708605&r1=1708604&r2=1708605&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/LocalStrings.properties
(original)
+++ tomcat/trunk/java/org/apache/coyote/http11/LocalStrings.properties Wed Oct
14 14:29:31 2015
@@ -20,6 +20,7 @@ abstractHttp11Protocol.httpUpgradeConfig
http11processor.fallToDebug=\n Note: further occurrences of HTTP header
parsing errors will be logged at DEBUG level.
http11processor.header.parse=Error parsing HTTP request header
http11processor.neverused=This method should never be used
+http11processor.pushrequest.notsupported=Server push requests are not
supported by the HTTP/1.1 protocol
http11processor.request.prepare=Error preparing request
http11processor.request.process=Error processing request
http11processor.request.finish=Error finishing request
Modified: tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java?rev=1708605&r1=1708604&r2=1708605&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java
(original)
+++ tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java Wed Oct
14 14:29:31 2015
@@ -129,6 +129,7 @@ public class Http2UpgradeHandler extends
// Start at -1 so the 'add 2' logic in closeIdleStreams() works
private volatile int maxActiveRemoteStreamId = -1;
private volatile int maxProcessedStreamId;
+ private final AtomicInteger nextLocalStreamId = new AtomicInteger(2);
private final PingManager pingManager = new PingManager();
private volatile int newStreamsSinceLastPrune = 0;
// Tracking for when the connection is blocked (windowSize < 1)
@@ -499,6 +500,46 @@ public class Http2UpgradeHandler extends
}
+ void writePushHeaders(Stream stream, int pushedStreamId, Request
coyoteRequest, int payloadSize)
+ throws IOException {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.writePushHeaders",
connectionId,
+ stream.getIdentifier()));
+ }
+ // This ensures the Stream processing thread has control of the socket.
+ synchronized (socketWrapper) {
+ byte[] header = new byte[9];
+ ByteBuffer target = ByteBuffer.allocate(payloadSize);
+ boolean first = true;
+ State state = null;
+ byte[] pushedStreamIdBytes = new byte[4];
+ ByteUtil.set31Bits(pushedStreamIdBytes, 0, pushedStreamId);
+ target.put(pushedStreamIdBytes);
+ while (state != State.COMPLETE) {
+ state =
getHpackEncoder().encode(coyoteRequest.getMimeHeaders(), target);
+ target.flip();
+ ByteUtil.setThreeBytes(header, 0, target.limit());
+ if (first) {
+ first = false;
+ header[3] = FrameType.PUSH_PROMISE.getIdByte();
+ } else {
+ header[3] = FrameType.CONTINUATION.getIdByte();
+ }
+ if (state == State.COMPLETE) {
+ header[4] += FLAG_END_OF_HEADERS;
+ }
+ if (log.isDebugEnabled()) {
+ log.debug(target.limit() + " bytes");
+ }
+ ByteUtil.set31Bits(header, 5,
stream.getIdentifier().intValue());
+ socketWrapper.write(true, header, 0, header.length);
+ socketWrapper.write(true, target.array(),
target.arrayOffset(), target.limit());
+ socketWrapper.flush(true);
+ }
+ }
+ }
+
+
private HpackEncoder getHpackEncoder() {
if (hpackEncoder == null) {
hpackEncoder = new
HpackEncoder(localSettings.getHeaderTableSize());
@@ -598,6 +639,10 @@ public class Http2UpgradeHandler extends
synchronized (stream) {
do {
synchronized (this) {
+ if (!stream.canWrite()) {
+ throw new IOException("TODO i18n: Stream not
writeable");
+ }
+
long windowSize = getWindowSize();
if (windowSize < 1 || backLogSize > 0) {
// Has this stream been granted an allocation
@@ -808,6 +853,18 @@ public class Http2UpgradeHandler extends
}
+ private Stream createLocalStream(Request request) {
+ int streamId = nextLocalStreamId.getAndAdd(2);
+
+ Integer key = Integer.valueOf(streamId);
+
+ Stream result = new Stream(key, this, request);
+ streams.put(key, result);
+ maxRemoteStreamId = streamId;
+ return result;
+ }
+
+
private void close() {
connectionState.set(ConnectionState.CLOSED);
try {
@@ -890,6 +947,21 @@ public class Http2UpgradeHandler extends
}
+ void push(Request request, Stream associatedStream) throws IOException {
+ Stream pushStream = createLocalStream(request);
+
+ // TODO: Is 1k the optimal value?
+ writePushHeaders(associatedStream,
pushStream.getIdentifier().intValue(), request, 1024);
+
+ pushStream.sentPushPromise();
+
+ // Process this stream on a container thread
+ StreamProcessor streamProcessor = new StreamProcessor(pushStream,
adapter, socketWrapper);
+ streamProcessor.setSslSupport(sslSupport);
+ socketWrapper.getEndpoint().getExecutor().execute(streamProcessor);
+ }
+
+
String getProperty(String key) {
return socketWrapper.getEndpoint().getProperty(key);
}
@@ -968,7 +1040,11 @@ public class Http2UpgradeHandler extends
return false;
}
} else if (thisRead == -1) {
- throw new EOFException();
+ if (connectionState.get().isNewStreamAllowed()) {
+ throw new EOFException();
+ } else {
+ return false;
+ }
} else {
pos += thisRead;
len -= thisRead;
Modified: tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties?rev=1708605&r1=1708604&r2=1708605&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties Wed Oct
14 14:29:31 2015
@@ -117,6 +117,7 @@ upgradeHandler.windowSizeTooBig=Connecti
upgradeHandler.windowSizeReservationInterrupted=Connection [{0}], Stream
[{1}], reservation for [{2}] bytes
upgradeHandler.writeBody=Connection [{0}], Stream [{1}], Data length [{2}]
upgradeHandler.writeHeaders=Connection [{0}], Stream [{1}]
+upgradeHandler.writePushHeaders=Connection [{0}], Stream [{1}]
writeStateMachine.endWrite.ise=It is illegal to specify [{0}] for the new
state once a write has completed
writeStateMachine.ise=It is illegal to call [{0}()] in state [{1}]
\ No newline at end of file
Modified: tomcat/trunk/java/org/apache/coyote/http2/Stream.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/Stream.java?rev=1708605&r1=1708604&r2=1708605&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/Stream.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/Stream.java Wed Oct 14 14:29:31
2015
@@ -145,6 +145,9 @@ public class Stream extends AbstractStre
private synchronized int reserveWindowSize(int reservation, boolean block)
throws IOException {
long windowSize = getWindowSize();
while (windowSize < 1) {
+ if (!canWrite()) {
+ throw new IOException("TODO i18n: Stream not writeable");
+ }
try {
if (block) {
wait();
@@ -329,11 +332,21 @@ public class Stream extends AbstractStre
}
+ void sentPushPromise() {
+ state.sentPushPromise();
+ }
+
+
boolean isActive() {
return state.isActive();
}
+ boolean canWrite() {
+ return state.canWrite();
+ }
+
+
boolean isClosedFinal() {
return state.isClosedFinal();
}
@@ -365,6 +378,19 @@ public class Stream extends AbstractStre
}
+ void push(Request request) throws IOException {
+ // Set the special HTTP/2 headers
+
request.getMimeHeaders().addValue(":method").duplicate(request.method());
+
request.getMimeHeaders().addValue(":scheme").duplicate(request.scheme());
+ // TODO: Query string
+
request.getMimeHeaders().addValue(":path").duplicate(request.decodedURI());
+ // TODO: Handle default ports
+ request.getMimeHeaders().addValue(":authority").setString(
+ request.serverName().getString() + ":" +
request.getServerPort());
+ handler.push(request, this);
+ }
+
+
class StreamOutputBuffer implements OutputBuffer {
private final ByteBuffer buffer = ByteBuffer.allocate(8 * 1024);
Modified: tomcat/trunk/java/org/apache/coyote/http2/StreamProcessor.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/StreamProcessor.java?rev=1708605&r1=1708604&r2=1708605&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/StreamProcessor.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/StreamProcessor.java Wed Oct 14
14:29:31 2015
@@ -31,6 +31,7 @@ import org.apache.coyote.Adapter;
import org.apache.coyote.AsyncContextCallback;
import org.apache.coyote.ContainerThreadMarker;
import org.apache.coyote.ErrorState;
+import org.apache.coyote.Request;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.ByteChunk;
@@ -385,6 +386,17 @@ public class StreamProcessor extends Abs
break;
}
+ // Servlet 4.0 Push requests
+ case PUSH_REQUEST: {
+ try {
+ stream.push((Request) param);
+ } catch (IOException ioe) {
+ response.setErrorException(ioe);
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, ioe);
+ }
+ break;
+ }
+
// Unsupported / illegal under HTTP/2
case UPGRADE:
throw new UnsupportedOperationException(
Added: tomcat/trunk/webapps/examples/WEB-INF/classes/http2/SimpleImagePush.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/classes/http2/SimpleImagePush.java?rev=1708605&view=auto
==============================================================================
--- tomcat/trunk/webapps/examples/WEB-INF/classes/http2/SimpleImagePush.java
(added)
+++ tomcat/trunk/webapps/examples/WEB-INF/classes/http2/SimpleImagePush.java
Wed Oct 14 14:29:31 2015
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package http2;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.PushBuilder;
+
+public class SimpleImagePush extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+
+ PushBuilder pb =
req.getPushBuilder().setPath("servlets/images/code.gif");
+ pb.push();
+
+ resp.setCharacterEncoding("UTF-8");
+ resp.setContentType("text/html");
+ PrintWriter pw = resp.getWriter();
+ pw.println("<html>");
+ pw.println("<body>");
+ pw.println("<p>The following image was provided via a push
request.</p>");
+ pw.println("<img src=\"" + req.getContextPath() +
"/servlets/images/code.gif\"/>");
+ pw.println("</body>");
+ pw.println("</html>");
+ pw.flush();
+ }
+}
Propchange:
tomcat/trunk/webapps/examples/WEB-INF/classes/http2/SimpleImagePush.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: tomcat/trunk/webapps/examples/WEB-INF/web.xml
URL:
http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/web.xml?rev=1708605&r1=1708604&r2=1708605&view=diff
==============================================================================
--- tomcat/trunk/webapps/examples/WEB-INF/web.xml (original)
+++ tomcat/trunk/webapps/examples/WEB-INF/web.xml Wed Oct 14 14:29:31 2015
@@ -379,6 +379,16 @@
<url-pattern>/servlets/nonblocking/numberwriter</url-pattern>
</servlet-mapping>
+ <!-- Server Push examples -->
+ <servlet>
+ <servlet-name>simpleimagepush</servlet-name>
+ <servlet-class>http2.SimpleImagePush</servlet-class>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>simpleimagepush</servlet-name>
+ <url-pattern>/servlets/serverpush/simpleimage</url-pattern>
+ </servlet-mapping>
+
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.xhtml</welcome-file>
Modified: tomcat/trunk/webapps/examples/servlets/index.html
URL:
http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/servlets/index.html?rev=1708605&r1=1708604&r2=1708605&view=diff
==============================================================================
--- tomcat/trunk/webapps/examples/servlets/index.html (original)
+++ tomcat/trunk/webapps/examples/servlets/index.html Wed Oct 14 14:29:31 2015
@@ -166,6 +166,17 @@ for clarity.</p>
<td style="width: 30%;"></td>
</tr>
+<tr>
+ <th colspan="3">Servlet 4.0 Server Push examples</th>
+</tr>
+<tr>
+ <td>Simple image push</td>
+ <td style="width: 30%;">
+ <a href="serverpush/simpleimage"><img src="images/execute.gif" alt="">
Execute</a>
+ </td>
+ <td style="width: 30%;"></td>
+</tr>
+
</table>
</body>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]