HttpServletResponse Writer在flush上发送消息长度

时间:2017-03-21 14:23:09

标签: java servlets streaming embedded-jetty

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

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

github

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

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);
    }
}

其中
&#34; \ r \ N26 \ r \ n&#34; - 是消息长度的HEX表示
&#34; event:put \ ndata:{... rty&#39;:&#39; value&#39;} \ n \ n&#34; - 实际发送的消息

如何关闭长度转移?

代码:

<?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>

依赖关系:

GVRCameraSceneObject cameraObject = null;
    try {
        cameraObject = new GVRCameraSceneObject(gvrContext, 3.6f, 2.0f);
        cameraObject.setUpCameraForVrMode(1); // set up 60 fps camera preview.
    } catch (GVRCameraSceneObject.GVRCameraAccessException e) {
        // Cannot open camera
        Log.e(TAG, "Cannot open the camera",e);
    }
cameraObject.getTransform().setPosition(0.0f, 0.0f, -4.0f);
scene.getMainCameraRig().addChildObject(object);

1 个答案:

答案 0 :(得分:1)

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

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

如果您有HTTP / 1.1客户端,并且无法在服务器端设置Content-Length标头,请考虑设置Connection: close请求和/或响应标头,因为这将取消持久连接模式触发分块传输编码。

  

建议:支持Chunked传输编码会很聪明,因为你可以从任意数量的位置(甚至是代理!)看到它。