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: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to