mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
continue netty 4 — we MUST write LastHttpContent or use FullHttpResponse
This commit is contained in:
@@ -24,9 +24,9 @@ import io.netty.handler.codec.http.QueryStringDecoder;
|
||||
abstract class DelegatingHttpRequestHandlerBase extends SimpleChannelInboundHandler<FullHttpRequest> {
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext context, FullHttpRequest message) throws Exception {
|
||||
//if (BuiltInServer.LOG.isDebugEnabled()) {
|
||||
//BuiltInServer.LOG.debug(request.toString());
|
||||
//}
|
||||
if (BuiltInServer.LOG.isDebugEnabled()) {
|
||||
BuiltInServer.LOG.debug("IN HTTP:\n" + message);
|
||||
}
|
||||
|
||||
if (!process(context, message, new QueryStringDecoder(message.getUri()))) {
|
||||
Responses.sendStatus(HttpResponseStatus.NOT_FOUND, context.channel(), message);
|
||||
|
||||
@@ -32,6 +32,7 @@ import java.io.RandomAccessFile;
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
|
||||
import static org.jetbrains.io.Responses.*;
|
||||
|
||||
public class FileResponses {
|
||||
@@ -46,7 +47,7 @@ public class FileResponses {
|
||||
if (!StringUtil.isEmpty(ifModifiedSince)) {
|
||||
try {
|
||||
if (Responses.DATE_FORMAT.get().parse(ifModifiedSince).getTime() >= lastModified) {
|
||||
sendStatus(HttpResponseStatus.NOT_MODIFIED, channel, request);
|
||||
send(response(HttpResponseStatus.NOT_MODIFIED), channel, request);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -67,7 +68,8 @@ public class FileResponses {
|
||||
RandomAccessFile raf = new RandomAccessFile(file, "r");
|
||||
try {
|
||||
long fileLength = raf.length();
|
||||
HttpResponse response = create(getContentType(file.getPath()));
|
||||
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
|
||||
response.headers().add(CONTENT_TYPE, getContentType(file.getPath()));
|
||||
addCommonHeaders(response);
|
||||
response.headers().set(HttpHeaders.Names.CACHE_CONTROL, "private, must-revalidate");
|
||||
response.headers().set(HttpHeaders.Names.LAST_MODIFIED, Responses.DATE_FORMAT.get().format(new Date(file.lastModified())));
|
||||
@@ -76,22 +78,22 @@ public class FileResponses {
|
||||
HttpHeaders.setContentLength(response, fileLength);
|
||||
}
|
||||
|
||||
ChannelFuture future = channel.write(response);
|
||||
channel.write(response);
|
||||
if (request.getMethod() != HttpMethod.HEAD) {
|
||||
if (channel.pipeline().get(SslHandler.class) == null) {
|
||||
// No encryption - use zero-copy
|
||||
future = channel.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength));
|
||||
channel.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength));
|
||||
}
|
||||
else {
|
||||
// Cannot use zero-copy with HTTPS
|
||||
future = channel.write(new ChunkedFile(raf, 0, fileLength, 8192));
|
||||
channel.write(new ChunkedFile(raf, 0, fileLength, 8192));
|
||||
}
|
||||
}
|
||||
|
||||
ChannelFuture future = channel.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
|
||||
if (!keepAlive) {
|
||||
future.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
channel.flush();
|
||||
|
||||
fileWillBeClosed = true;
|
||||
}
|
||||
|
||||
@@ -17,12 +17,11 @@ package org.jetbrains.io;
|
||||
|
||||
import com.intellij.openapi.util.AtomicNotNullLazyValue;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.handler.codec.compression.JZlibEncoder;
|
||||
import io.netty.handler.codec.compression.JdkZlibDecoder;
|
||||
import io.netty.handler.codec.compression.ZlibWrapper;
|
||||
import io.netty.handler.codec.http.HttpMessage;
|
||||
import io.netty.handler.ssl.SslHandler;
|
||||
import io.netty.handler.stream.ChunkedWriteHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -105,6 +104,17 @@ final class PortUnificationServerHandler extends Decoder<ByteBuf> {
|
||||
else {
|
||||
NettyUtil.initHttpHandlers(pipeline);
|
||||
pipeline.addLast(delegatingHttpRequestHandler);
|
||||
if (BuiltInServer.LOG.isDebugEnabled()) {
|
||||
pipeline.addLast(new ChannelOutboundHandlerAdapter() {
|
||||
@Override
|
||||
public void write(ChannelHandlerContext context, Object message, ChannelPromise promise) throws Exception {
|
||||
if (message instanceof HttpMessage) {
|
||||
BuiltInServer.LOG.debug("OUT HTTP:\n" + message);
|
||||
}
|
||||
super.write(context, message, promise);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// must be after new channels handlers addition (netty bug?)
|
||||
|
||||
@@ -52,6 +52,19 @@ public final class Responses {
|
||||
|
||||
private static String SERVER_HEADER_VALUE;
|
||||
|
||||
public static FullHttpResponse response(HttpResponseStatus status) {
|
||||
return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, Unpooled.EMPTY_BUFFER);
|
||||
}
|
||||
|
||||
public static HttpResponse create(@Nullable String contentType, @Nullable ByteBuf content) {
|
||||
HttpResponse response =
|
||||
new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content == null ? Unpooled.EMPTY_BUFFER : content);
|
||||
if (contentType != null) {
|
||||
response.headers().add(CONTENT_TYPE, contentType);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
public static void addAllowAnyOrigin(HttpResponse response) {
|
||||
response.headers().add(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
||||
}
|
||||
@@ -88,18 +101,10 @@ public final class Responses {
|
||||
}
|
||||
}
|
||||
|
||||
public static void send(String contentType, CharSequence content, Channel channel, HttpRequest request) {
|
||||
send(create(contentType, Unpooled.copiedBuffer(content, CharsetUtil.UTF_8)), channel, request);
|
||||
}
|
||||
|
||||
public static void send(HttpResponse response, Channel channel, @Nullable HttpRequest request) {
|
||||
if (response.getStatus() != HttpResponseStatus.NOT_MODIFIED) {
|
||||
if (response instanceof FullHttpResponse) {
|
||||
HttpHeaders.setContentLength(response, ((FullHttpResponse)response).content().readableBytes());
|
||||
}
|
||||
else if (!HttpHeaders.isContentLengthSet(response)) {
|
||||
HttpHeaders.setContentLength(response, 0);
|
||||
}
|
||||
if (response.getStatus() != HttpResponseStatus.NOT_MODIFIED && !HttpHeaders.isContentLengthSet(response)) {
|
||||
HttpHeaders.setContentLength(response,
|
||||
response instanceof FullHttpResponse ? ((FullHttpResponse)response).content().readableBytes() : 0);
|
||||
}
|
||||
|
||||
addCommonHeaders(response);
|
||||
@@ -120,34 +125,12 @@ public final class Responses {
|
||||
addAllowAnyOrigin(response);
|
||||
}
|
||||
|
||||
public static HttpResponse create(String contentType) {
|
||||
return create(contentType, null);
|
||||
}
|
||||
|
||||
public static HttpResponse create(@Nullable String contentType, @Nullable ByteBuf content) {
|
||||
HttpResponse response = content == null
|
||||
? new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)
|
||||
: new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
|
||||
if (contentType != null) {
|
||||
response.headers().add(CONTENT_TYPE, contentType);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
public static void send(CharSequence content, Channel channel, @Nullable HttpRequest request) {
|
||||
send(content, CharsetUtil.US_ASCII, channel, request);
|
||||
}
|
||||
|
||||
public static void send(CharSequence content, Charset charset, Channel channel, @Nullable HttpRequest request) {
|
||||
send(create(null, Unpooled.copiedBuffer(content, charset)), channel, request);
|
||||
}
|
||||
|
||||
public static void send(HttpResponse response, Channel channel) {
|
||||
send(response, channel, true);
|
||||
}
|
||||
|
||||
public static void send(HttpResponseStatus status, ChannelHandlerContext context) {
|
||||
send(new DefaultHttpResponse(HttpVersion.HTTP_1_1, status), context.channel());
|
||||
send(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer(content, charset)), channel, request);
|
||||
}
|
||||
|
||||
private static void send(HttpResponse response, Channel channel, boolean close) {
|
||||
@@ -155,7 +138,11 @@ public final class Responses {
|
||||
return;
|
||||
}
|
||||
|
||||
ChannelFuture future = channel.writeAndFlush(response);
|
||||
ChannelFuture future = channel.write(response);
|
||||
if (!(response instanceof FullHttpResponse)) {
|
||||
channel.write(LastHttpContent.EMPTY_LAST_CONTENT);
|
||||
}
|
||||
channel.flush();
|
||||
if (close) {
|
||||
future.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
@@ -173,14 +160,9 @@ public final class Responses {
|
||||
send(createStatusResponse(responseStatus, request, description), channel, request);
|
||||
}
|
||||
|
||||
public static void sendStatus(HttpResponse response, HttpRequest request, Channel channel) {
|
||||
response.headers().add(CONTENT_TYPE, "text/html");
|
||||
send(response, channel, request);
|
||||
}
|
||||
|
||||
private static HttpResponse createStatusResponse(HttpResponseStatus responseStatus, @Nullable HttpRequest request, @Nullable String description) {
|
||||
if (request != null && request.getMethod() == HttpMethod.HEAD) {
|
||||
return new DefaultHttpResponse(HttpVersion.HTTP_1_1, responseStatus);
|
||||
return response(responseStatus);
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
@@ -197,8 +179,7 @@ public final class Responses {
|
||||
}
|
||||
|
||||
public static void sendOptionsResponse(String allowHeaders, HttpRequest request, ChannelHandlerContext context) {
|
||||
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
|
||||
addAllowAnyOrigin(response);
|
||||
HttpResponse response = response(HttpResponseStatus.OK);
|
||||
response.headers().set(ACCESS_CONTROL_ALLOW_METHODS, allowHeaders);
|
||||
response.headers().set(ALLOW, allowHeaders);
|
||||
send(response, context.channel(), request);
|
||||
|
||||
Reference in New Issue
Block a user