convert DelegatingHttpRequestHandler, DelegatingHttpRequestHandlerBase, FileResponses, Responses to kotlin

This commit is contained in:
Vladimir Krivosheev
2016-04-11 12:17:51 +02:00
parent e51f4336b8
commit c3cbe3cd48
4 changed files with 235 additions and 266 deletions
@@ -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<HttpRequestHandler>("DelegatingHttpRequestHandler.handler")
@ChannelHandler.Sharable
final class DelegatingHttpRequestHandler extends DelegatingHttpRequestHandlerBase {
private static final AttributeKey<HttpRequestHandler> PREV_HANDLER = AttributeKey.valueOf("DelegatingHttpRequestHandler.handler");
@Override
protected boolean process(@NotNull ChannelHandlerContext context,
@NotNull FullHttpRequest request,
@NotNull QueryStringDecoder urlDecoder) throws IOException, ImageWriteException {
Attribute<HttpRequestHandler> 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)
}
}
}
@@ -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<FullHttpRequest> {
@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<FullHttpRequest>() {
@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())
}
}
@@ -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)
}
}
}
@@ -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("<!doctype html><title>").append(message).append("</title>").append("<h1 style=\"text-align: center\">").append(message).append("</h1>")
if (description != null) {
builder.append("<p>").append(description).append("</p>")
}
builder.append("<hr/><p style=\"text-align: center\">").append(StringUtil.notNullize(serverHeaderValue, "")).append("</p>")
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("<!doctype html><title>").append(message).append("</title>").append("<h1 style=\"text-align: center\">").append(message).append("</h1>");
if (description != null) {
builder.append("<p>").append(description).append("</p>");
}
builder.append("<hr/><p style=\"text-align: center\">").append(StringUtil.notNullize(getServerHeaderValue(), "")).append("</p>");
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
}