diff --git a/platform/platform-impl/src/org/jetbrains/io/DelegatingHttpRequestHandler.java b/platform/platform-impl/src/org/jetbrains/io/DelegatingHttpRequestHandler.java index ca4cc3fbb0a8..bfad9825627f 100644 --- a/platform/platform-impl/src/org/jetbrains/io/DelegatingHttpRequestHandler.java +++ b/platform/platform-impl/src/org/jetbrains/io/DelegatingHttpRequestHandler.java @@ -13,83 +13,76 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.io; +package org.jetbrains.io -import com.intellij.openapi.application.ex.ApplicationInfoEx; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.IconLoader; -import com.intellij.util.ui.UIUtil; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.QueryStringDecoder; -import io.netty.util.Attribute; -import io.netty.util.AttributeKey; -import org.apache.sanselan.ImageFormat; -import org.apache.sanselan.ImageWriteException; -import org.apache.sanselan.Sanselan; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.ide.HttpRequestHandler; +import com.intellij.openapi.application.ex.ApplicationInfoEx +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.util.IconLoader +import com.intellij.util.ui.UIUtil +import io.netty.buffer.Unpooled +import io.netty.channel.ChannelHandler +import io.netty.channel.ChannelHandlerContext +import io.netty.handler.codec.http.FullHttpRequest +import io.netty.handler.codec.http.QueryStringDecoder +import io.netty.util.AttributeKey +import org.apache.sanselan.ImageFormat +import org.apache.sanselan.Sanselan +import org.jetbrains.ide.HttpRequestHandler -import javax.swing.*; -import java.awt.image.BufferedImage; -import java.io.IOException; +import java.awt.image.BufferedImage + +private val PREV_HANDLER = AttributeKey.valueOf("DelegatingHttpRequestHandler.handler") @ChannelHandler.Sharable -final class DelegatingHttpRequestHandler extends DelegatingHttpRequestHandlerBase { - private static final AttributeKey PREV_HANDLER = AttributeKey.valueOf("DelegatingHttpRequestHandler.handler"); - - @Override - protected boolean process(@NotNull ChannelHandlerContext context, - @NotNull FullHttpRequest request, - @NotNull QueryStringDecoder urlDecoder) throws IOException, ImageWriteException { - Attribute prevHandlerAttribute = context.attr(PREV_HANDLER); - HttpRequestHandler connectedHandler = prevHandlerAttribute.get(); +internal class DelegatingHttpRequestHandler : DelegatingHttpRequestHandlerBase() { + override fun process(context: ChannelHandlerContext, + request: FullHttpRequest, + urlDecoder: QueryStringDecoder): Boolean { + val prevHandlerAttribute = context.attr(PREV_HANDLER) + val connectedHandler = prevHandlerAttribute.get() if (connectedHandler != null) { if (connectedHandler.isSupported(request) && connectedHandler.process(urlDecoder, request, context)) { - return true; + return true } // prev cached connectedHandler is not suitable for this request, so, let's find it again - prevHandlerAttribute.set(null); + prevHandlerAttribute.set(null) } - for (HttpRequestHandler handler : HttpRequestHandler.EP_NAME.getExtensions()) { + for (handler in HttpRequestHandler.EP_NAME.extensions) { try { if (handler.isSupported(request) && handler.process(urlDecoder, request, context)) { - prevHandlerAttribute.set(handler); - return true; + prevHandlerAttribute.set(handler) + return true } } - catch (Throwable e) { - Logger.getInstance(BuiltInServer.class).error(e); + catch (e: Throwable) { + Logger.getInstance(BuiltInServer::class.java).error(e) } + } - if (urlDecoder.path().equals("/favicon.ico")) { - Icon icon = IconLoader.findIcon(ApplicationInfoEx.getInstanceEx().getSmallIconUrl()); + if (urlDecoder.path() == "/favicon.ico") { + val icon = IconLoader.findIcon(ApplicationInfoEx.getInstanceEx().smallIconUrl) if (icon != null) { - BufferedImage image = UIUtil.createImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB); - icon.paintIcon(null, image.getGraphics(), 0, 0); - byte[] icoBytes = Sanselan.writeImageToBytes(image, ImageFormat.IMAGE_FORMAT_ICO, null); - HttpResponse response = Responses.response(FileResponses.getContentType(urlDecoder.path()), Unpooled.wrappedBuffer(icoBytes)); - Responses.addNoCache(response); - Responses.send(response, context.channel(), request); - return true; + val image = UIUtil.createImage(icon.iconWidth, icon.iconHeight, BufferedImage.TYPE_INT_ARGB) + icon.paintIcon(null, image.graphics, 0, 0) + val icoBytes = Sanselan.writeImageToBytes(image, ImageFormat.IMAGE_FORMAT_ICO, null) + response(FileResponses.getContentType(urlDecoder.path()), Unpooled.wrappedBuffer(icoBytes)) + .addNoCache() + .send(context.channel(), request) + return true } } - return false; + return false } - @Override - public void exceptionCaught(@NotNull ChannelHandlerContext context, @NotNull Throwable cause) { + override fun exceptionCaught(context: ChannelHandlerContext, cause: Throwable) { try { - context.attr(PREV_HANDLER).remove(); + context.attr(PREV_HANDLER).remove() } finally { - super.exceptionCaught(context, cause); + super.exceptionCaught(context, cause) } } } \ No newline at end of file diff --git a/platform/platform-impl/src/org/jetbrains/io/DelegatingHttpRequestHandlerBase.java b/platform/platform-impl/src/org/jetbrains/io/DelegatingHttpRequestHandlerBase.java index e4b36ad0fc65..794a7ae8a777 100644 --- a/platform/platform-impl/src/org/jetbrains/io/DelegatingHttpRequestHandlerBase.java +++ b/platform/platform-impl/src/org/jetbrains/io/DelegatingHttpRequestHandlerBase.java @@ -13,33 +13,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.io; +package org.jetbrains.io -import com.intellij.openapi.diagnostic.Logger; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.QueryStringDecoder; -import org.jetbrains.annotations.NotNull; +import com.intellij.openapi.diagnostic.Logger +import io.netty.channel.ChannelHandlerContext +import io.netty.handler.codec.http.FullHttpRequest +import io.netty.handler.codec.http.HttpResponseStatus +import io.netty.handler.codec.http.QueryStringDecoder -abstract class DelegatingHttpRequestHandlerBase extends SimpleChannelInboundHandlerAdapter { - @Override - protected void messageReceived(ChannelHandlerContext context, FullHttpRequest message) throws Exception { - if (Logger.getInstance(BuiltInServer.class).isDebugEnabled()) { - Logger.getInstance(BuiltInServer.class).debug("IN HTTP: " + message.uri()); +internal abstract class DelegatingHttpRequestHandlerBase : SimpleChannelInboundHandlerAdapter() { + @Throws(Exception::class) + override fun messageReceived(context: ChannelHandlerContext, message: FullHttpRequest) { + if (Logger.getInstance(BuiltInServer::class.java).isDebugEnabled) { + Logger.getInstance(BuiltInServer::class.java).debug("IN HTTP: " + message.uri()) } - if (!process(context, message, new QueryStringDecoder(message.uri()))) { - Responses.sendStatus(HttpResponseStatus.NOT_FOUND, context.channel(), message); + if (!process(context, message, QueryStringDecoder(message.uri()))) { + HttpResponseStatus.NOT_FOUND.sendStatus(context.channel(), message) } } - protected abstract boolean process(@NotNull ChannelHandlerContext context, - @NotNull FullHttpRequest request, - @NotNull QueryStringDecoder urlDecoder) throws Exception; + @Throws(Exception::class) + protected abstract fun process(context: ChannelHandlerContext, + request: FullHttpRequest, + urlDecoder: QueryStringDecoder): Boolean - @Override - public void exceptionCaught(@NotNull ChannelHandlerContext context, @NotNull Throwable cause) { - NettyUtil.logAndClose(cause, Logger.getInstance(BuiltInServer.class), context.channel()); + override fun exceptionCaught(context: ChannelHandlerContext, cause: Throwable) { + NettyUtil.logAndClose(cause, Logger.getInstance(BuiltInServer::class.java), context.channel()) } } \ No newline at end of file diff --git a/platform/platform-impl/src/org/jetbrains/io/FileResponses.java b/platform/platform-impl/src/org/jetbrains/io/FileResponses.java index 28bf29cb5eda..b710bdc96379 100644 --- a/platform/platform-impl/src/org/jetbrains/io/FileResponses.java +++ b/platform/platform-impl/src/org/jetbrains/io/FileResponses.java @@ -13,102 +13,91 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.io; +package org.jetbrains.io -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.DefaultFileRegion; -import io.netty.handler.codec.http.*; -import io.netty.handler.ssl.SslHandler; -import io.netty.handler.stream.ChunkedFile; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import io.netty.channel.Channel +import io.netty.channel.ChannelFutureListener +import io.netty.channel.DefaultFileRegion +import io.netty.handler.codec.http.* +import io.netty.handler.ssl.SslHandler +import io.netty.handler.stream.ChunkedFile +import java.io.FileNotFoundException +import java.io.RandomAccessFile +import java.nio.file.Files +import java.nio.file.Path +import java.util.* +import javax.activation.MimetypesFileTypeMap -import javax.activation.MimetypesFileTypeMap; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Date; +private val FILE_MIMETYPE_MAP = MimetypesFileTypeMap() -import static org.jetbrains.io.Responses.*; - -public class FileResponses { - private static final MimetypesFileTypeMap FILE_MIMETYPE_MAP = new MimetypesFileTypeMap(); - - public static String getContentType(String path) { - return FILE_MIMETYPE_MAP.getContentType(path); +object FileResponses { + fun getContentType(path: String): String { + return FILE_MIMETYPE_MAP.getContentType(path) } - private static boolean checkCache(@NotNull HttpRequest request, @NotNull Channel channel, long lastModified) { - Long ifModified = request.headers().getTimeMillis(HttpHeaderNames.IF_MODIFIED_SINCE); + private fun checkCache(request: HttpRequest, channel: Channel, lastModified: Long): Boolean { + val ifModified = request.headers().getTimeMillis(HttpHeaderNames.IF_MODIFIED_SINCE) if (ifModified != null && ifModified >= lastModified) { - send(response(HttpResponseStatus.NOT_MODIFIED), channel, request); - return true; + response(HttpResponseStatus.NOT_MODIFIED).send(channel, request) + return true } - return false; + return false } - @Nullable - public static HttpResponse prepareSend(@NotNull HttpRequest request, @NotNull Channel channel, long lastModified, @NotNull String filename) { + fun prepareSend(request: HttpRequest, channel: Channel, lastModified: Long, filename: String): HttpResponse? { if (checkCache(request, channel, lastModified)) { - return null; + return null } - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - response.headers().set(HttpHeaderNames.CONTENT_TYPE, getContentType(filename)); - addCommonHeaders(response); - response.headers().set(HttpHeaderNames.CACHE_CONTROL, "private, must-revalidate"); - response.headers().set(HttpHeaderNames.LAST_MODIFIED, new Date(lastModified)); - return response; + val response = DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK) + response.headers().set(HttpHeaderNames.CONTENT_TYPE, getContentType(filename)) + response.addCommonHeaders() + response.headers().set(HttpHeaderNames.CACHE_CONTROL, "private, must-revalidate") + response.headers().set(HttpHeaderNames.LAST_MODIFIED, Date(lastModified)) + return response } - public static void sendFile(@NotNull HttpRequest request, @NotNull Channel channel, @NotNull Path file) throws IOException { - HttpResponse response = prepareSend(request, channel, Files.getLastModifiedTime(file).toMillis(), file.getFileName().toString()); - if (response == null) { - return; - } + fun sendFile(request: HttpRequest, channel: Channel, file: Path) { + val response = prepareSend(request, channel, Files.getLastModifiedTime(file).toMillis(), file.fileName.toString()) ?: return - boolean keepAlive = addKeepAliveIfNeed(response, request); + val keepAlive = response.addKeepAliveIfNeed(request) - boolean fileWillBeClosed = false; - RandomAccessFile raf; + var fileWillBeClosed = false + val raf: RandomAccessFile try { - raf = new RandomAccessFile(file.toFile(), "r"); + raf = RandomAccessFile(file.toFile(), "r") } - catch (FileNotFoundException ignored) { - send(response(HttpResponseStatus.NOT_FOUND), channel, request); - return; + catch (ignored: FileNotFoundException) { + response(HttpResponseStatus.NOT_FOUND).send(channel, request) + return } try { - long fileLength = raf.length(); - HttpUtil.setContentLength(response, fileLength); + val fileLength = raf.length() + HttpUtil.setContentLength(response, fileLength) - channel.write(response); - if (request.method() != HttpMethod.HEAD) { - if (channel.pipeline().get(SslHandler.class) == null) { + channel.write(response) + if (request.method() !== HttpMethod.HEAD) { + if (channel.pipeline().get(SslHandler::class.java) == null) { // no encryption - use zero-copy - channel.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength)); + channel.write(DefaultFileRegion(raf.channel, 0, fileLength)) } else { // cannot use zero-copy with HTTPS - channel.write(new ChunkedFile(raf)); + channel.write(ChunkedFile(raf)) } } - fileWillBeClosed = true; + fileWillBeClosed = true } finally { if (!fileWillBeClosed) { - raf.close(); + raf.close() } } - ChannelFuture future = channel.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); + val future = channel.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT) if (!keepAlive) { - future.addListener(ChannelFutureListener.CLOSE); + future.addListener(ChannelFutureListener.CLOSE) } } } \ No newline at end of file diff --git a/platform/platform-impl/src/org/jetbrains/io/Responses.java b/platform/platform-impl/src/org/jetbrains/io/Responses.java index 5872f6aaebe1..62c2117b1f8f 100644 --- a/platform/platform-impl/src/org/jetbrains/io/Responses.java +++ b/platform/platform-impl/src/org/jetbrains/io/Responses.java @@ -1,159 +1,147 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. - * - * Licensed 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.jetbrains.io; +* Copyright 2000-2015 JetBrains s.r.o. +* +* Licensed 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. +*/ +@file:JvmName("Responses") +package org.jetbrains.io -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ex.ApplicationInfoEx; -import com.intellij.openapi.util.text.StringUtil; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.handler.codec.http.*; -import io.netty.util.CharsetUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.ex.ApplicationInfoEx +import com.intellij.openapi.util.text.StringUtil +import io.netty.buffer.ByteBuf +import io.netty.buffer.ByteBufAllocator +import io.netty.buffer.ByteBufUtil +import io.netty.buffer.Unpooled +import io.netty.channel.Channel +import io.netty.channel.ChannelFutureListener +import io.netty.handler.codec.http.* +import io.netty.util.CharsetUtil +import java.nio.CharBuffer +import java.nio.charset.Charset +import java.util.* -import java.nio.CharBuffer; -import java.nio.charset.Charset; -import java.util.Calendar; +private var SERVER_HEADER_VALUE: String? = null -public final class Responses { - private static String SERVER_HEADER_VALUE; +fun response(status: HttpResponseStatus): FullHttpResponse { + return DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, Unpooled.EMPTY_BUFFER) +} - public static FullHttpResponse response(HttpResponseStatus status) { - return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, Unpooled.EMPTY_BUFFER); +fun response(contentType: String?, content: ByteBuf?): FullHttpResponse { + val response = if (content == null) + DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK) + else + DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content) + if (contentType != null) { + response.headers().set(HttpHeaderNames.CONTENT_TYPE, contentType) } + return response +} - @NotNull - public static FullHttpResponse response(@Nullable String contentType, @Nullable ByteBuf content) { - FullHttpResponse response = content == null - ? new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK) - : new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content); - if (contentType != null) { - response.headers().set(HttpHeaderNames.CONTENT_TYPE, contentType); - } - return response; +fun HttpResponse.setDate() { + if (!headers().contains(HttpHeaderNames.DATE)) { + headers().set(HttpHeaderNames.DATE, Calendar.getInstance().time) } +} - public static void setDate(@NotNull HttpResponse response) { - if (!response.headers().contains(HttpHeaderNames.DATE)) { - response.headers().set(HttpHeaderNames.DATE, Calendar.getInstance().getTime()); - } - } +fun HttpResponse.addNoCache(): HttpResponse { + headers().add(HttpHeaderNames.CACHE_CONTROL, "no-cache, no-store, must-revalidate, max-age=0") + headers().add(HttpHeaderNames.PRAGMA, "no-cache") + return this +} - public static void addNoCache(@NotNull HttpResponse response) { - response.headers().add(HttpHeaderNames.CACHE_CONTROL, "no-cache, no-store, must-revalidate, max-age=0"); - response.headers().add(HttpHeaderNames.PRAGMA, "no-cache"); - } - - @Nullable - public static String getServerHeaderValue() { +val serverHeaderValue: String? + get() { if (SERVER_HEADER_VALUE == null) { - Application app = ApplicationManager.getApplication(); - if (app != null && !app.isDisposed()) { - SERVER_HEADER_VALUE = ApplicationInfoEx.getInstanceEx().getFullApplicationName(); + val app = ApplicationManager.getApplication() + if (app != null && !app.isDisposed) { + SERVER_HEADER_VALUE = ApplicationInfoEx.getInstanceEx().fullApplicationName } } - return SERVER_HEADER_VALUE; + return SERVER_HEADER_VALUE } - public static void addServer(@NotNull HttpResponse response) { - if (getServerHeaderValue() != null) { - response.headers().add(HttpHeaderNames.SERVER, getServerHeaderValue()); - } +fun HttpResponse.addServer() { + serverHeaderValue?.let { + headers().add(HttpHeaderNames.SERVER, it) + } +} + +fun HttpResponse.send(channel: Channel, request: HttpRequest?) { + if (status() !== HttpResponseStatus.NOT_MODIFIED && !HttpUtil.isContentLengthSet(this)) { + HttpUtil.setContentLength(this, + (if (this is FullHttpResponse) content().readableBytes() else 0).toLong()) } - public static void send(@NotNull HttpResponse response, Channel channel, @Nullable HttpRequest request) { - if (response.status() != HttpResponseStatus.NOT_MODIFIED && !HttpUtil.isContentLengthSet(response)) { - HttpUtil.setContentLength(response, - response instanceof FullHttpResponse ? ((FullHttpResponse)response).content().readableBytes() : 0); - } + addCommonHeaders() + send(channel, request != null && !addKeepAliveIfNeed(request)) +} - addCommonHeaders(response); - send(response, channel, request != null && !addKeepAliveIfNeed(response, request)); +fun HttpResponse.addKeepAliveIfNeed(request: HttpRequest): Boolean { + if (HttpUtil.isKeepAlive(request)) { + HttpUtil.setKeepAlive(this, true) + return true + } + return false +} + +fun HttpResponse.addCommonHeaders() { + addServer() + setDate() +} + +fun send(content: CharSequence, channel: Channel, request: HttpRequest?) { + send(content, CharsetUtil.US_ASCII, channel, request) +} + +fun send(content: CharSequence, charset: Charset, channel: Channel, request: HttpRequest? = null) { + DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer(content, charset)).send(channel, request) +} + +fun HttpResponse.send(channel: Channel, close: Boolean) { + if (!channel.isActive) { + return } - public static boolean addKeepAliveIfNeed(HttpResponse response, HttpRequest request) { - if (HttpUtil.isKeepAlive(request)) { - HttpUtil.setKeepAlive(response, true); - return true; - } - return false; + val future = channel.write(this) + if (this !is FullHttpResponse) { + channel.write(LastHttpContent.EMPTY_LAST_CONTENT) + } + channel.flush() + if (close) { + future.addListener(ChannelFutureListener.CLOSE) + } +} + +@JvmOverloads +fun HttpResponseStatus.sendStatus(channel: Channel, request: HttpRequest? = null, description: String? = null) { + createStatusResponse(this, request, description).send(channel, request) +} + +private fun createStatusResponse(responseStatus: HttpResponseStatus, request: HttpRequest?, description: String?): HttpResponse { + if (request != null && request.method() === HttpMethod.HEAD) { + return response(responseStatus) } - public static void addCommonHeaders(@NotNull HttpResponse response) { - addServer(response); - setDate(response); + val builder = StringBuilder() + val message = responseStatus.toString() + builder.append("").append(message).append("").append("

").append(message).append("

") + if (description != null) { + builder.append("

").append(description).append("

") } + builder.append("

").append(StringUtil.notNullize(serverHeaderValue, "")).append("

") - 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(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer(content, charset)), channel, request); - } - - public static void send(@NotNull HttpResponse response, @NotNull Channel channel, boolean close) { - if (!channel.isActive()) { - return; - } - - ChannelFuture future = channel.write(response); - if (!(response instanceof FullHttpResponse)) { - channel.write(LastHttpContent.EMPTY_LAST_CONTENT); - } - channel.flush(); - if (close) { - future.addListener(ChannelFutureListener.CLOSE); - } - } - - public static void sendStatus(HttpResponseStatus responseStatus, Channel channel) { - sendStatus(responseStatus, channel, null); - } - - public static void sendStatus(HttpResponseStatus responseStatus, Channel channel, @Nullable HttpRequest request) { - sendStatus(responseStatus, channel, null, request); - } - - public static void sendStatus(@NotNull HttpResponseStatus responseStatus, Channel channel, @Nullable String description, @Nullable HttpRequest request) { - send(createStatusResponse(responseStatus, request, description), channel, request); - } - - private static HttpResponse createStatusResponse(HttpResponseStatus responseStatus, @Nullable HttpRequest request, @Nullable String description) { - if (request != null && request.method() == HttpMethod.HEAD) { - return response(responseStatus); - } - - StringBuilder builder = new StringBuilder(); - String message = responseStatus.toString(); - builder.append("").append(message).append("").append("

").append(message).append("

"); - if (description != null) { - builder.append("

").append(description).append("

"); - } - builder.append("

").append(StringUtil.notNullize(getServerHeaderValue(), "")).append("

"); - - DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, responseStatus, ByteBufUtil.encodeString(ByteBufAllocator.DEFAULT, CharBuffer.wrap(builder), CharsetUtil.UTF_8)); - response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html"); - return response; - } + val response = DefaultFullHttpResponse(HttpVersion.HTTP_1_1, responseStatus, ByteBufUtil.encodeString(ByteBufAllocator.DEFAULT, CharBuffer.wrap(builder), CharsetUtil.UTF_8)) + response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html") + return response } \ No newline at end of file