HttpServletResponse Writer 在刷新时发送消息长度

HttpServletResponse Writer sends message length on flush

我在数据流方面遇到了一些问题 在刷新常规数据块时,服务器发送它的长度实际上破坏了数据流。

(嵌入式码头和嵌入式 tomcat 产生相同的结果。)

github

我使用 netty 作为客户端,它将数字记录为传入块的第一个字节(用 \n\r 分隔的常规行)。 还有码头的记录器

DEBUG org.eclipse.jetty.io.WriteFlusher - write: WriteFlusher@ ... [HeapByteBuffer@ ... {<<<\r\n26\r\n>>>...},DirectByteBuffer@...{<<<event: put\ndata:{...rty':'value'}\n\n>>>...}]

在哪里
"\r\n26\r\n" - 是消息长度的十六进制表示
"event: put\ndata:{...rty':'value'}\n\n" - 消息实际发送

如何关闭长度传输?

代码:

public class TestCorruptedWrites {

    private static Server server = null;
    private final static int port = 8089;
    private final static String endpoint = "/test";
    static String message = "event: put\ndata:{'property':'value'}\n\n";

    private static void sleep(int millis){
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @BeforeClass
    public static void initMockServer() throws Exception {
        System.out.println(message.length());
        final ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
        contextHandler.addServlet(new ServletHolder(new HttpServlet() {
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                resp.setCharacterEncoding(StandardCharsets.UTF_8.name());
                resp.flushBuffer();

                Writer writer = resp.getWriter();
                while (true) {
                    writer.write(message);
                    writer.flush();
                    sleep(300);
                }
            }
        }), endpoint);
        contextHandler.setContextPath("/");

        server = new Server(new QueuedThreadPool(50, 10));
        ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory());
        connector.setPort(port);
        server.addConnector(connector);
        server.setHandler(contextHandler);
        server.start();
    }

    @Test
    public void testClient() throws InterruptedException, IOException {
        InternalLoggerFactory.setDefaultFactory(new Slf4JLoggerFactory());

        final Bootstrap bootstrap = new Bootstrap();
        final URI uri = URI.create("http://localhost:" + port + endpoint);

        bootstrap
                .group(new NioEventLoopGroup())
                .channel(NioSocketChannel.class)
                .remoteAddress(new InetSocketAddress(uri.getHost(), uri.getPort()))
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel channel) throws Exception {
                        ChannelPipeline pipeline = channel.pipeline();
                        pipeline.addLast("logger", new LoggingHandler(LogLevel.DEBUG));
                        pipeline.addLast("line", new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Delimiters.lineDelimiter()));
                        pipeline.addLast("string", new StringDecoder());
                        pipeline.addLast("encoder", new HttpRequestEncoder());
                        pipeline.addLast("log", new SimpleChannelInboundHandler<String>(){
                            @Override
                            protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
                                System.out.println(" ---> " + msg);
                            }
                            @Override
                            public void channelActive(ChannelHandlerContext context) {
                                System.out.println("active!");
                                HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri.toString());
                                request.headers().add(HttpHeaders.Names.ACCEPT, "text/event-stream");
                                request.headers().add(HttpHeaders.Names.HOST, uri.getHost());
                                request.headers().add(HttpHeaders.Names.ORIGIN, "http://" + uri.getHost());
                                context.channel().writeAndFlush(request);
                            }
                        });
                    }
                })
                .connect();
        Thread.sleep(500_000);
    }
}

依赖关系:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.test</groupId>
  <artifactId>test</artifactId>
  <version>1.0-SNAPSHOT</version>

  <dependencies>

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.2</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.0.9</version>
    </dependency>

    <dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-all</artifactId>
      <version>5.0.0.Alpha1</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
    </dependency>
    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-servlet</artifactId>
      <version>9.4.2.v20170220</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

这是标准 HTTP/1.1 您看到的分块传输编码。

如果您的响应没有 Content-Length header,则会发生分块,并且连接将保持持久(根据 HTTP/1.1 规范)。

如果你有一个HTTP/1.1客户端,并且不能在服务器端设置Content-Length header,考虑设置Connection: close请求and/or 响应 headers,因为这将取消触发分块传输编码的持久连接模式。

Advice: it would be smart to support Chunked transfer encoding, as you can see that from any number of locations (even proxies!)