HttpServletResponse Writer 在刷新时发送消息长度
HttpServletResponse Writer sends message length on flush
我在数据流方面遇到了一些问题
在刷新常规数据块时,服务器发送它的长度实际上破坏了数据流。
(嵌入式码头和嵌入式 tomcat 产生相同的结果。)
我使用 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!)
我在数据流方面遇到了一些问题 在刷新常规数据块时,服务器发送它的长度实际上破坏了数据流。
(嵌入式码头和嵌入式 tomcat 产生相同的结果。)
我使用 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!)