| /* |
| * 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 org.apache.coyote.http2; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.PrintWriter; |
| import java.nio.ByteBuffer; |
| import java.nio.charset.StandardCharsets; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import jakarta.servlet.AsyncContext; |
| import jakarta.servlet.ServletException; |
| import jakarta.servlet.http.HttpServlet; |
| import jakarta.servlet.http.HttpServletRequest; |
| import jakarta.servlet.http.HttpServletResponse; |
| |
| import org.junit.Assert; |
| import org.junit.Test; |
| |
| import org.apache.catalina.Context; |
| import org.apache.catalina.Wrapper; |
| import org.apache.catalina.connector.Connector; |
| import org.apache.catalina.connector.ResponseFacade; |
| import org.apache.catalina.startup.Tomcat; |
| import org.apache.tomcat.util.compat.JrePlatform; |
| import org.apache.tomcat.util.http.FastHttpDateFormat; |
| import org.apache.tomcat.util.http.Method; |
| |
| public class TestStreamProcessor extends Http2TestBase { |
| |
| @Test |
| public void testAsyncComplete() throws Exception { |
| enableHttp2(); |
| |
| Tomcat tomcat = getTomcatInstance(); |
| |
| // Map the async servlet to /simple so we can re-use the HTTP/2 handling |
| // logic from the super class. |
| Context ctxt = getProgrammaticRootContext(); |
| Tomcat.addServlet(ctxt, "simple", new SimpleServlet()); |
| ctxt.addServletMappingDecoded("/simple", "simple"); |
| Wrapper w = Tomcat.addServlet(ctxt, "async", new AsyncComplete()); |
| w.setAsyncSupported(true); |
| ctxt.addServletMappingDecoded("/async", "async"); |
| |
| tomcat.start(); |
| |
| openClientConnection(); |
| doHttpUpgrade(); |
| sendClientPreface(); |
| validateHttp2InitialResponse(); |
| |
| byte[] frameHeader = new byte[9]; |
| ByteBuffer headersPayload = ByteBuffer.allocate(128); |
| buildGetRequest(frameHeader, headersPayload, null, 3, "/async"); |
| writeFrame(frameHeader, headersPayload); |
| |
| readSimpleGetResponse(); |
| // Flush before startAsync means body is written in two packets so an |
| // additional frame needs to be read |
| parser.readFrame(); |
| |
| Assert.assertEquals("3-HeadersStart\n" + "3-Header-[:status]-[200]\n" + |
| "3-Header-[content-type]-[text/plain;charset=UTF-8]\n" + |
| "3-Header-[date]-[Wed, 11 Nov 2015 19:18:42 GMT]\n" + "3-HeadersEnd\n" + "3-Body-17\n" + "3-Body-8\n" + |
| "3-EndOfStream\n", output.getTrace()); |
| } |
| |
| |
| @Test |
| public void testAsyncDispatch() throws Exception { |
| enableHttp2(); |
| |
| Tomcat tomcat = getTomcatInstance(); |
| |
| // Map the async servlet to /simple so we can re-use the HTTP/2 handling |
| // logic from the super class. |
| Context ctxt = getProgrammaticRootContext(); |
| Tomcat.addServlet(ctxt, "simple", new SimpleServlet()); |
| ctxt.addServletMappingDecoded("/simple", "simple"); |
| Wrapper w = Tomcat.addServlet(ctxt, "async", new AsyncDispatch()); |
| w.setAsyncSupported(true); |
| ctxt.addServletMappingDecoded("/async", "async"); |
| |
| tomcat.start(); |
| |
| openClientConnection(); |
| doHttpUpgrade(); |
| sendClientPreface(); |
| validateHttp2InitialResponse(); |
| |
| byte[] frameHeader = new byte[9]; |
| ByteBuffer headersPayload = ByteBuffer.allocate(128); |
| buildGetRequest(frameHeader, headersPayload, null, 3, "/async"); |
| writeFrame(frameHeader, headersPayload); |
| |
| readSimpleGetResponse(); |
| Assert.assertEquals(getSimpleResponseTrace(3), output.getTrace()); |
| } |
| |
| |
| @Test |
| public void testPrepareHeaders() throws Exception { |
| enableHttp2(); |
| |
| Tomcat tomcat = getTomcatInstance(); |
| |
| File appDir = new File("test/webapp"); |
| Context ctxt = tomcat.addWebapp(null, "", appDir.getAbsolutePath()); |
| |
| Tomcat.addServlet(ctxt, "simple", new SimpleServlet()); |
| ctxt.addServletMappingDecoded("/simple", "simple"); |
| |
| tomcat.start(); |
| |
| openClientConnection(); |
| doHttpUpgrade(); |
| sendClientPreface(); |
| validateHttp2InitialResponse(); |
| |
| byte[] frameHeader = new byte[9]; |
| ByteBuffer headersPayload = ByteBuffer.allocate(128); |
| |
| List<Header> headers = new ArrayList<>(3); |
| headers.add(new Header(":method", Method.GET)); |
| headers.add(new Header(":scheme", "http")); |
| headers.add(new Header(":path", "/index.html")); |
| headers.add(new Header(":authority", "localhost:" + getPort())); |
| headers.add(new Header("if-modified-since", FastHttpDateFormat.getCurrentDate())); |
| |
| buildGetRequest(frameHeader, headersPayload, null, headers, 3); |
| |
| writeFrame(frameHeader, headersPayload); |
| |
| parser.readFrame(); |
| |
| StringBuilder expected = new StringBuilder(); |
| expected.append("3-HeadersStart\n"); |
| expected.append("3-Header-[:status]-[304]\n"); |
| // Different line-endings -> different files size -> different weak eTag |
| if (JrePlatform.IS_WINDOWS) { |
| expected.append("3-Header-[etag]-[W/\"957-1447269522000\"]\n"); |
| } else { |
| expected.append("3-Header-[etag]-[W/\"934-1447269522000\"]\n"); |
| } |
| expected.append("3-Header-[date]-[Wed, 11 Nov 2015 19:18:42 GMT]\n"); |
| expected.append("3-HeadersEnd\n"); |
| expected.append("3-EndOfStream\n"); |
| |
| Assert.assertEquals(expected.toString(), output.getTrace()); |
| } |
| |
| |
| @Test |
| public void testPrepareHeadersNoContent() throws Exception { |
| enableHttp2(); |
| |
| Tomcat tomcat = getTomcatInstance(); |
| |
| File appDir = new File("test/webapp"); |
| Context ctxt = tomcat.addWebapp(null, "", appDir.getAbsolutePath()); |
| |
| Tomcat.addServlet(ctxt, "simple", new SimpleServlet()); |
| ctxt.addServletMappingDecoded("/simple", "simple"); |
| Tomcat.addServlet(ctxt, "noContent", new NoContentServlet()); |
| ctxt.addServletMappingDecoded("/noContent", "noContent"); |
| |
| |
| tomcat.start(); |
| |
| openClientConnection(); |
| doHttpUpgrade(); |
| sendClientPreface(); |
| validateHttp2InitialResponse(); |
| |
| byte[] frameHeader = new byte[9]; |
| ByteBuffer headersPayload = ByteBuffer.allocate(128); |
| |
| List<Header> headers = new ArrayList<>(3); |
| headers.add(new Header(":method", Method.GET)); |
| headers.add(new Header(":scheme", "http")); |
| headers.add(new Header(":path", "/noContent")); |
| headers.add(new Header(":authority", "localhost:" + getPort())); |
| |
| buildGetRequest(frameHeader, headersPayload, null, headers, 3); |
| |
| writeFrame(frameHeader, headersPayload); |
| |
| parser.readFrame(); |
| |
| StringBuilder expected = new StringBuilder(); |
| expected.append("3-HeadersStart\n"); |
| expected.append("3-Header-[:status]-[204]\n"); |
| expected.append("3-Header-[date]-[Wed, 11 Nov 2015 19:18:42 GMT]\n"); |
| expected.append("3-HeadersEnd\n"); |
| expected.append("3-EndOfStream\n"); |
| |
| Assert.assertEquals(expected.toString(), output.getTrace()); |
| } |
| |
| |
| @Test |
| public void testValidateRequestMethod() throws Exception { |
| enableHttp2(); |
| |
| Tomcat tomcat = getTomcatInstance(); |
| |
| File appDir = new File("test/webapp"); |
| Context ctxt = tomcat.addWebapp(null, "", appDir.getAbsolutePath()); |
| |
| Tomcat.addServlet(ctxt, "simple", new SimpleServlet()); |
| ctxt.addServletMappingDecoded("/simple", "simple"); |
| |
| tomcat.start(); |
| |
| openClientConnection(); |
| doHttpUpgrade(); |
| sendClientPreface(); |
| validateHttp2InitialResponse(); |
| |
| byte[] frameHeader = new byte[9]; |
| ByteBuffer headersPayload = ByteBuffer.allocate(128); |
| |
| List<Header> headers = new ArrayList<>(4); |
| headers.add(new Header(":method", "not,token")); |
| headers.add(new Header(":scheme", "http")); |
| headers.add(new Header(":path", "/index.html")); |
| headers.add(new Header(":authority", "localhost:" + getPort())); |
| |
| buildGetRequest(frameHeader, headersPayload, null, headers, 3); |
| |
| writeFrame(frameHeader, headersPayload); |
| |
| parser.readFrame(); |
| |
| StringBuilder expected = new StringBuilder(); |
| expected.append("3-HeadersStart\n"); |
| expected.append("3-Header-[:status]-[400]\n"); |
| expected.append("3-Header-[date]-[Wed, 11 Nov 2015 19:18:42 GMT]\n"); |
| expected.append("3-HeadersEnd\n"); |
| |
| Assert.assertEquals(expected.toString(), output.getTrace()); |
| } |
| |
| |
| @Test |
| public void testValidateRequestHeaderName() throws Exception { |
| enableHttp2(); |
| |
| Tomcat tomcat = getTomcatInstance(); |
| |
| File appDir = new File("test/webapp"); |
| Context ctxt = tomcat.addWebapp(null, "", appDir.getAbsolutePath()); |
| |
| Tomcat.addServlet(ctxt, "simple", new SimpleServlet()); |
| ctxt.addServletMappingDecoded("/simple", "simple"); |
| |
| tomcat.start(); |
| |
| openClientConnection(); |
| doHttpUpgrade(); |
| sendClientPreface(); |
| validateHttp2InitialResponse(); |
| |
| byte[] frameHeader = new byte[9]; |
| ByteBuffer headersPayload = ByteBuffer.allocate(128); |
| |
| List<Header> headers = new ArrayList<>(5); |
| headers.add(new Header(":method", Method.GET)); |
| headers.add(new Header(":scheme", "http")); |
| headers.add(new Header(":path", "/index.html")); |
| headers.add(new Header(":authority", "localhost:" + getPort())); |
| headers.add(new Header("not token", "value")); |
| |
| buildGetRequest(frameHeader, headersPayload, null, headers, 3); |
| |
| writeFrame(frameHeader, headersPayload); |
| |
| parser.readFrame(); |
| |
| StringBuilder expected = new StringBuilder(); |
| expected.append("3-HeadersStart\n"); |
| expected.append("3-Header-[:status]-[400]\n"); |
| expected.append("3-Header-[date]-[Wed, 11 Nov 2015 19:18:42 GMT]\n"); |
| expected.append("3-HeadersEnd\n"); |
| |
| Assert.assertEquals(expected.toString(), output.getTrace()); |
| } |
| |
| |
| @Test |
| public void testValidateRequestURI() throws Exception { |
| enableHttp2(); |
| |
| Tomcat tomcat = getTomcatInstance(); |
| |
| File appDir = new File("test/webapp"); |
| Context ctxt = tomcat.addWebapp(null, "", appDir.getAbsolutePath()); |
| |
| Tomcat.addServlet(ctxt, "simple", new SimpleServlet()); |
| ctxt.addServletMappingDecoded("/simple", "simple"); |
| |
| tomcat.start(); |
| |
| openClientConnection(); |
| doHttpUpgrade(); |
| sendClientPreface(); |
| validateHttp2InitialResponse(); |
| |
| byte[] frameHeader = new byte[9]; |
| ByteBuffer headersPayload = ByteBuffer.allocate(128); |
| |
| List<Header> headers = new ArrayList<>(4); |
| headers.add(new Header(":method", Method.GET)); |
| headers.add(new Header(":scheme", "http")); |
| headers.add(new Header(":path", "/index^html")); |
| headers.add(new Header(":authority", "localhost:" + getPort())); |
| |
| buildGetRequest(frameHeader, headersPayload, null, headers, 3); |
| |
| writeFrame(frameHeader, headersPayload); |
| |
| parser.readFrame(); |
| |
| StringBuilder expected = new StringBuilder(); |
| expected.append("3-HeadersStart\n"); |
| expected.append("3-Header-[:status]-[400]\n"); |
| expected.append("3-Header-[date]-[Wed, 11 Nov 2015 19:18:42 GMT]\n"); |
| expected.append("3-HeadersEnd\n"); |
| |
| Assert.assertEquals(expected.toString(), output.getTrace()); |
| } |
| |
| |
| @Test |
| public void testValidateRequestQueryString() throws Exception { |
| enableHttp2(); |
| |
| Tomcat tomcat = getTomcatInstance(); |
| |
| File appDir = new File("test/webapp"); |
| Context ctxt = tomcat.addWebapp(null, "", appDir.getAbsolutePath()); |
| |
| Tomcat.addServlet(ctxt, "simple", new SimpleServlet()); |
| ctxt.addServletMappingDecoded("/simple", "simple"); |
| |
| tomcat.start(); |
| |
| openClientConnection(); |
| doHttpUpgrade(); |
| sendClientPreface(); |
| validateHttp2InitialResponse(); |
| |
| byte[] frameHeader = new byte[9]; |
| ByteBuffer headersPayload = ByteBuffer.allocate(128); |
| |
| List<Header> headers = new ArrayList<>(4); |
| headers.add(new Header(":method", Method.GET)); |
| headers.add(new Header(":scheme", "http")); |
| headers.add(new Header(":path", "/index.html?foo=[]")); |
| headers.add(new Header(":authority", "localhost:" + getPort())); |
| |
| buildGetRequest(frameHeader, headersPayload, null, headers, 3); |
| |
| writeFrame(frameHeader, headersPayload); |
| |
| parser.readFrame(); |
| |
| StringBuilder expected = new StringBuilder(); |
| expected.append("3-HeadersStart\n"); |
| expected.append("3-Header-[:status]-[400]\n"); |
| expected.append("3-Header-[date]-[Wed, 11 Nov 2015 19:18:42 GMT]\n"); |
| expected.append("3-HeadersEnd\n"); |
| |
| Assert.assertEquals(expected.toString(), output.getTrace()); |
| } |
| |
| |
| @Test |
| public void testValidateRequestQueryStringRelaxed() throws Exception { |
| enableHttp2(); |
| |
| Tomcat tomcat = getTomcatInstance(); |
| |
| File appDir = new File("test/webapp"); |
| Context ctxt = tomcat.addWebapp(null, "", appDir.getAbsolutePath()); |
| |
| Tomcat.addServlet(ctxt, "simple", new SimpleServlet()); |
| ctxt.addServletMappingDecoded("/simple", "simple"); |
| |
| tomcat.getConnector().setProperty("relaxedQueryChars", "[]"); |
| |
| tomcat.start(); |
| |
| openClientConnection(); |
| doHttpUpgrade(); |
| sendClientPreface(); |
| validateHttp2InitialResponse(); |
| |
| byte[] frameHeader = new byte[9]; |
| ByteBuffer headersPayload = ByteBuffer.allocate(128); |
| |
| List<Header> headers = new ArrayList<>(4); |
| headers.add(new Header(":method", Method.GET)); |
| headers.add(new Header(":scheme", "http")); |
| headers.add(new Header(":path", "/index.html?foo=[]")); |
| headers.add(new Header(":authority", "localhost:" + getPort())); |
| |
| buildGetRequest(frameHeader, headersPayload, null, headers, 3); |
| |
| writeFrame(frameHeader, headersPayload); |
| |
| parser.readFrame(); |
| |
| StringBuilder expected = new StringBuilder(); |
| expected.append("3-HeadersStart\n"); |
| expected.append("3-Header-[:status]-[200]\n"); |
| |
| // The status code is the most important thing to test |
| Assert.assertTrue(output.getTrace().startsWith(expected.toString())); |
| } |
| |
| |
| private static final class AsyncComplete extends HttpServlet { |
| |
| private static final long serialVersionUID = 1L; |
| |
| @Override |
| protected void doGet(HttpServletRequest request, HttpServletResponse response) |
| throws ServletException, IOException { |
| |
| response.setContentType("text/plain"); |
| response.setCharacterEncoding("UTF-8"); |
| |
| PrintWriter pw = response.getWriter(); |
| pw.print("Enter-"); |
| |
| final AsyncContext asyncContext = request.startAsync(request, response); |
| pw.print("StartAsync-"); |
| pw.flush(); |
| |
| asyncContext.start(new Runnable() { |
| |
| @Override |
| public void run() { |
| try { |
| asyncContext.getResponse().getWriter().print("Complete"); |
| asyncContext.complete(); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| }); |
| } |
| } |
| |
| |
| private static final class AsyncDispatch extends HttpServlet { |
| |
| private static final long serialVersionUID = 1L; |
| |
| @Override |
| protected void doGet(HttpServletRequest request, HttpServletResponse response) |
| throws ServletException, IOException { |
| |
| final AsyncContext asyncContext = request.startAsync(request, response); |
| asyncContext.start(new Runnable() { |
| |
| @Override |
| public void run() { |
| try { |
| asyncContext.dispatch("/simple"); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| }); |
| } |
| } |
| |
| |
| @Test |
| public void testCompression() throws Exception { |
| enableHttp2(); |
| |
| Tomcat tomcat = getTomcatInstance(); |
| |
| Context ctxt = getProgrammaticRootContext(); |
| Tomcat.addServlet(ctxt, "simple", new SimpleServlet()); |
| ctxt.addServletMappingDecoded("/simple", "simple"); |
| Tomcat.addServlet(ctxt, "compression", new CompressionServlet()); |
| ctxt.addServletMappingDecoded("/compression", "compression"); |
| |
| // Enable compression |
| Connector connector = tomcat.getConnector(); |
| Assert.assertTrue(connector.setProperty("compression", "on")); |
| |
| tomcat.start(); |
| |
| enableHttp2(); |
| openClientConnection(); |
| doHttpUpgrade(); |
| sendClientPreface(); |
| validateHttp2InitialResponse(); |
| |
| |
| byte[] frameHeader = new byte[9]; |
| ByteBuffer headersPayload = ByteBuffer.allocate(128); |
| |
| List<Header> headers = new ArrayList<>(3); |
| headers.add(new Header(":method", Method.GET)); |
| headers.add(new Header(":scheme", "http")); |
| headers.add(new Header(":path", "/compression")); |
| headers.add(new Header(":authority", "localhost:" + getPort())); |
| headers.add(new Header("accept-encoding", "gzip")); |
| |
| buildGetRequest(frameHeader, headersPayload, null, headers, 3); |
| |
| writeFrame(frameHeader, headersPayload); |
| |
| readSimpleGetResponse(); |
| |
| Assert.assertEquals("3-HeadersStart\n" + "3-Header-[:status]-[200]\n" + "3-Header-[vary]-[accept-encoding]\n" + |
| "3-Header-[content-encoding]-[gzip]\n" + "3-Header-[content-type]-[text/plain;charset=UTF-8]\n" + |
| "3-Header-[date]-[Wed, 11 Nov 2015 19:18:42 GMT]\n" + "3-HeadersEnd\n" + "3-Body-97\n" + |
| "3-EndOfStream\n", output.getTrace()); |
| } |
| |
| |
| private static class CompressionServlet extends HttpServlet { |
| |
| private static final long serialVersionUID = 1L; |
| |
| @Override |
| protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { |
| // Generate content type that is compressible |
| resp.setContentType("text/plain"); |
| resp.setCharacterEncoding("UTF-8"); |
| |
| // Make ir large enough to trigger compression |
| int count = 64 * 1024; |
| |
| // One bytes per entry |
| resp.setContentLengthLong(count); |
| |
| OutputStream os = resp.getOutputStream(); |
| for (int i = 0; i < count; i++) { |
| os.write('X'); |
| } |
| } |
| } |
| |
| |
| @Test |
| public void testConnect() throws Exception { |
| http2Connect(); |
| |
| List<Header> headers = new ArrayList<>(4); |
| headers.add(new Header(":method", Method.CONNECT)); |
| headers.add(new Header(":scheme", "http")); |
| headers.add(new Header(":authority", "example.local")); |
| |
| byte[] headersFrameHeader = new byte[9]; |
| ByteBuffer headersPayload = ByteBuffer.allocate(128); |
| |
| buildGetRequest(headersFrameHeader, headersPayload, null, headers, 3); |
| |
| writeFrame(headersFrameHeader, headersPayload); |
| |
| parser.readFrame(); |
| |
| String trace = output.getTrace(); |
| Assert.assertTrue(trace, trace.contains("3-Header-[:status]-[501]")); |
| } |
| |
| |
| @Test |
| public void testEarlyHints() throws Exception { |
| enableHttp2(); |
| |
| Tomcat tomcat = getTomcatInstance(); |
| |
| Context ctxt = getProgrammaticRootContext(); |
| Tomcat.addServlet(ctxt, "simple", new SimpleServlet()); |
| ctxt.addServletMappingDecoded("/simple", "simple"); |
| Tomcat.addServlet(ctxt, "ehs", new EarlyHintsServlet()); |
| ctxt.addServletMappingDecoded("/ehs", "ehs"); |
| tomcat.start(); |
| |
| openClientConnection(); |
| doHttpUpgrade(); |
| sendClientPreface(); |
| validateHttp2InitialResponse(); |
| |
| // Disable overhead protection for window update as it breaks some tests |
| http2Protocol.setOverheadWindowUpdateThreshold(0); |
| |
| byte[] headersFrameHeader = new byte[9]; |
| ByteBuffer headersPayload = ByteBuffer.allocate(128); |
| |
| buildGetRequest(headersFrameHeader, headersPayload, null, 3, "/ehs"); |
| |
| // Write the headers |
| writeFrame(headersFrameHeader, headersPayload); |
| |
| parser.readFrame(); |
| |
| Assert.assertEquals("3-HeadersStart\n" + "3-Header-[:status]-[103]\n" + |
| "3-Header-[link]-[</style.css>; rel=preload; as=style]\n" + "3-HeadersEnd\n", output.getTrace()); |
| output.clearTrace(); |
| |
| parser.readFrame(); |
| parser.readFrame(); |
| |
| Assert.assertEquals("3-HeadersStart\n" + "3-Header-[:status]-[200]\n" + |
| "3-Header-[link]-[</style.css>; rel=preload; as=style]\n" + |
| "3-Header-[content-type]-[text/plain;charset=UTF-8]\n" + |
| "3-Header-[content-length]-[2]\n" + |
| "3-Header-[date]-[" + DEFAULT_DATE + "]\n" + "3-HeadersEnd\n" + "3-Body-2\n" + "3-EndOfStream\n", |
| output.getTrace()); |
| } |
| |
| |
| private static class EarlyHintsServlet extends HttpServlet { |
| |
| private static final long serialVersionUID = 1L; |
| |
| @Override |
| protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { |
| resp.addHeader("Link", "</style.css>; rel=preload; as=style"); |
| |
| ((ResponseFacade) resp).sendEarlyHints(); |
| |
| resp.setCharacterEncoding(StandardCharsets.UTF_8); |
| resp.setContentType("text/plain"); |
| |
| resp.getWriter().write("OK"); |
| } |
| } |
| |
| |
| @Test |
| public void testServerHeaderDefault() throws Exception { |
| enableHttp2(); |
| |
| Tomcat tomcat = getTomcatInstance(); |
| |
| Context ctxt = getProgrammaticRootContext(); |
| Tomcat.addServlet(ctxt, "simple", new SimpleServlet()); |
| ctxt.addServletMappingDecoded("/simple", "simple"); |
| Tomcat.addServlet(ctxt, "server", new ServerHeaderServlet()); |
| ctxt.addServletMappingDecoded("/server", "server"); |
| tomcat.start(); |
| |
| openClientConnection(); |
| doHttpUpgrade(); |
| sendClientPreface(); |
| validateHttp2InitialResponse(); |
| |
| // Disable overhead protection for window update as it breaks some tests |
| http2Protocol.setOverheadWindowUpdateThreshold(0); |
| |
| byte[] headersFrameHeader = new byte[9]; |
| ByteBuffer headersPayload = ByteBuffer.allocate(128); |
| |
| buildGetRequest(headersFrameHeader, headersPayload, null, 3, "/server"); |
| |
| // Write the headers |
| writeFrame(headersFrameHeader, headersPayload); |
| |
| parser.readFrame(); |
| parser.readFrame(); |
| |
| Assert.assertEquals("3-HeadersStart\n" + "3-Header-[:status]-[200]\n" + |
| "3-Header-[server]-[TestServerApp]\n" + |
| "3-Header-[content-type]-[text/plain;charset=UTF-8]\n" + |
| "3-Header-[content-length]-[2]\n" + |
| "3-Header-[date]-[" + DEFAULT_DATE + "]\n" + "3-HeadersEnd\n" + "3-Body-2\n" + "3-EndOfStream\n", |
| output.getTrace()); |
| } |
| |
| |
| @Test |
| public void testServerHeaderRemove() throws Exception { |
| enableHttp2(); |
| |
| Tomcat tomcat = getTomcatInstance(); |
| |
| tomcat.getConnector().setProperty("serverRemoveAppProvidedValues", "true"); |
| |
| Context ctxt = getProgrammaticRootContext(); |
| Tomcat.addServlet(ctxt, "simple", new SimpleServlet()); |
| ctxt.addServletMappingDecoded("/simple", "simple"); |
| Tomcat.addServlet(ctxt, "server", new ServerHeaderServlet()); |
| ctxt.addServletMappingDecoded("/server", "server"); |
| tomcat.start(); |
| |
| openClientConnection(); |
| doHttpUpgrade(); |
| sendClientPreface(); |
| validateHttp2InitialResponse(); |
| |
| // Disable overhead protection for window update as it breaks some tests |
| http2Protocol.setOverheadWindowUpdateThreshold(0); |
| |
| byte[] headersFrameHeader = new byte[9]; |
| ByteBuffer headersPayload = ByteBuffer.allocate(128); |
| |
| buildGetRequest(headersFrameHeader, headersPayload, null, 3, "/server"); |
| |
| // Write the headers |
| writeFrame(headersFrameHeader, headersPayload); |
| |
| parser.readFrame(); |
| parser.readFrame(); |
| |
| Assert.assertEquals("3-HeadersStart\n" + "3-Header-[:status]-[200]\n" + |
| "3-Header-[content-type]-[text/plain;charset=UTF-8]\n" + |
| "3-Header-[content-length]-[2]\n" + |
| "3-Header-[date]-[" + DEFAULT_DATE + "]\n" + "3-HeadersEnd\n" + "3-Body-2\n" + "3-EndOfStream\n", |
| output.getTrace()); |
| } |
| |
| |
| @Test |
| public void testServerHeaderForce() throws Exception { |
| enableHttp2(); |
| |
| Tomcat tomcat = getTomcatInstance(); |
| |
| Context ctxt = getProgrammaticRootContext(); |
| Tomcat.addServlet(ctxt, "simple", new SimpleServlet()); |
| ctxt.addServletMappingDecoded("/simple", "simple"); |
| Tomcat.addServlet(ctxt, "server", new ServerHeaderServlet()); |
| ctxt.addServletMappingDecoded("/server", "server"); |
| tomcat.start(); |
| |
| openClientConnection(); |
| doHttpUpgrade(); |
| sendClientPreface(); |
| validateHttp2InitialResponse(); |
| |
| /* |
| * This adds the server header to every response. Set this after the initial response has been validated to |
| * avoid having to update the validation code to account for the additional server header. |
| */ |
| tomcat.getConnector().setProperty("server", "TestServerForce"); |
| |
| // Disable overhead protection for window update as it breaks some tests |
| http2Protocol.setOverheadWindowUpdateThreshold(0); |
| |
| byte[] headersFrameHeader = new byte[9]; |
| ByteBuffer headersPayload = ByteBuffer.allocate(128); |
| |
| buildGetRequest(headersFrameHeader, headersPayload, null, 3, "/server"); |
| |
| // Write the headers |
| writeFrame(headersFrameHeader, headersPayload); |
| |
| parser.readFrame(); |
| parser.readFrame(); |
| |
| Assert.assertEquals("3-HeadersStart\n" + "3-Header-[:status]-[200]\n" + |
| "3-Header-[server]-[TestServerForce]\n" + |
| "3-Header-[content-type]-[text/plain;charset=UTF-8]\n" + |
| "3-Header-[content-length]-[2]\n" + |
| "3-Header-[date]-[" + DEFAULT_DATE + "]\n" + "3-HeadersEnd\n" + "3-Body-2\n" + "3-EndOfStream\n", |
| output.getTrace()); |
| } |
| |
| |
| private static class ServerHeaderServlet extends HttpServlet { |
| |
| private static final long serialVersionUID = 1L; |
| |
| @Override |
| protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { |
| resp.addHeader("server", "TestServerApp"); |
| |
| resp.setCharacterEncoding(StandardCharsets.UTF_8); |
| resp.setContentType("text/plain"); |
| |
| resp.getWriter().write("OK"); |
| } |
| } |
| } |