Add HTTP keep-alive support

Signed-off-by: Graham <gpe@openrs2.org>
Graham 4 years ago
parent 03fc499566
commit a43e98e8f4
  1. 17
      game/src/main/kotlin/org/openrs2/game/net/crossdomain/CrossDomainChannelHandler.kt
  2. 32
      game/src/main/kotlin/org/openrs2/game/net/http/Http.kt
  3. 17
      game/src/main/kotlin/org/openrs2/game/net/http/HttpChannelHandler.kt
  4. 3
      game/src/main/kotlin/org/openrs2/game/net/http/HttpChannelInitializer.kt
  5. 3
      game/src/main/kotlin/org/openrs2/game/net/login/LoginChannelHandler.kt

@ -7,6 +7,7 @@ import io.netty.channel.SimpleChannelInboundHandler
import io.netty.handler.codec.http.HttpMethod
import io.netty.handler.codec.http.HttpRequest
import io.netty.handler.codec.http.HttpResponseStatus
import io.netty.handler.timeout.IdleStateEvent
import org.openrs2.buffer.use
import org.openrs2.game.net.http.Http
@ -39,5 +40,21 @@ public object CrossDomainChannelHandler : SimpleChannelInboundHandler<HttpReques
override fun channelReadComplete(ctx: ChannelHandlerContext) {
ctx.flush()
if (ctx.channel().isWritable) {
ctx.read()
}
}
override fun channelWritabilityChanged(ctx: ChannelHandlerContext) {
if (ctx.channel().isWritable) {
ctx.read()
}
}
override fun userEventTriggered(ctx: ChannelHandlerContext, evt: Any) {
if (evt is IdleStateEvent) {
ctx.close()
}
}
}

@ -11,15 +11,22 @@ import io.netty.handler.codec.http.HttpMethod
import io.netty.handler.codec.http.HttpRequest
import io.netty.handler.codec.http.HttpResponseStatus
import io.netty.handler.codec.http.HttpVersion
import io.netty.handler.codec.http.LastHttpContent
import io.netty.util.ReferenceCounted
import org.openrs2.buffer.copiedBuffer
import org.openrs2.buffer.use
public object Http {
public const val TIMEOUT_SECS: Long = 30
public const val MAX_CONTENT_LENGTH: Int = 65536
public const val TEXT_X_CROSS_DOMAIN_POLICY: String = "text/x-cross-domain-policy"
private const val BANNER = "OpenRS2"
private fun isKeepAlive(request: HttpRequest, version: HttpVersion): Boolean {
val connection = request.headers().get(HttpHeaderNames.CONNECTION) ?: return version.isKeepAliveDefault
return HttpHeaderValues.KEEP_ALIVE.contentEquals(connection)
}
private fun writeResponse(
ctx: ChannelHandlerContext,
request: HttpRequest,
@ -34,17 +41,32 @@ public object Http {
HttpVersion.HTTP_1_1
}
val keepAlive = isKeepAlive(request, version)
val response = DefaultHttpResponse(version, status)
response.headers().add(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE)
response.headers().add(HttpHeaderNames.CONNECTION, if (keepAlive) {
HttpHeaderValues.KEEP_ALIVE
} else {
HttpHeaderValues.CLOSE
})
response.headers().add(HttpHeaderNames.SERVER, BANNER)
response.headers().add(HttpHeaderNames.CONTENT_TYPE, contentType)
response.headers().add(HttpHeaderNames.CONTENT_LENGTH, contentLength)
if (request.method() == HttpMethod.HEAD) {
ctx.write(response).addListener(ChannelFutureListener.CLOSE)
} else {
ctx.write(response, ctx.voidPromise())
ctx.write(content.retain()).addListener(ChannelFutureListener.CLOSE)
if (request.method() != HttpMethod.HEAD) {
ctx.write(content.retain(), ctx.voidPromise())
}
writeLastChunk(ctx, keepAlive)
}
private fun writeLastChunk(ctx: ChannelHandlerContext, keepAlive: Boolean) {
if (keepAlive) {
ctx.write(LastHttpContent.EMPTY_LAST_CONTENT, ctx.voidPromise())
} else {
ctx.write(LastHttpContent.EMPTY_LAST_CONTENT).addListener(ChannelFutureListener.CLOSE)
}
}

@ -6,6 +6,7 @@ import io.netty.channel.SimpleChannelInboundHandler
import io.netty.handler.codec.http.HttpHeaderValues
import io.netty.handler.codec.http.HttpRequest
import io.netty.handler.codec.http.HttpResponseStatus
import io.netty.handler.timeout.IdleStateEvent
import org.openrs2.buffer.use
import org.openrs2.game.net.FileProvider
import javax.inject.Inject
@ -39,5 +40,21 @@ public class HttpChannelHandler @Inject constructor(
override fun channelReadComplete(ctx: ChannelHandlerContext) {
ctx.flush()
if (ctx.channel().isWritable) {
ctx.read()
}
}
override fun channelWritabilityChanged(ctx: ChannelHandlerContext) {
if (ctx.channel().isWritable) {
ctx.read()
}
}
override fun userEventTriggered(ctx: ChannelHandlerContext, evt: Any) {
if (evt is IdleStateEvent) {
ctx.close()
}
}
}

@ -5,6 +5,8 @@ import io.netty.channel.ChannelInitializer
import io.netty.handler.codec.http.HttpObjectAggregator
import io.netty.handler.codec.http.HttpRequestDecoder
import io.netty.handler.codec.http.HttpResponseEncoder
import io.netty.handler.timeout.IdleStateHandler
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
@ -14,6 +16,7 @@ public class HttpChannelInitializer @Inject constructor(
) : ChannelInitializer<Channel>() {
override fun initChannel(ch: Channel) {
ch.pipeline().addLast(
IdleStateHandler(true, Http.TIMEOUT_SECS, Http.TIMEOUT_SECS, Http.TIMEOUT_SECS, TimeUnit.SECONDS),
HttpRequestDecoder(),
HttpResponseEncoder(),
HttpObjectAggregator(Http.MAX_CONTENT_LENGTH),

@ -9,6 +9,7 @@ import io.netty.handler.codec.http.HttpObjectAggregator
import io.netty.handler.codec.http.HttpRequestDecoder
import io.netty.handler.codec.http.HttpResponseEncoder
import io.netty.handler.codec.string.StringDecoder
import io.netty.handler.timeout.IdleStateHandler
import org.openrs2.buffer.copiedBuffer
import org.openrs2.game.net.crossdomain.CrossDomainChannelHandler
import org.openrs2.game.net.http.Http
@ -22,6 +23,7 @@ import org.openrs2.protocol.js5.Js5ResponseEncoder
import org.openrs2.protocol.js5.XorDecoder
import org.openrs2.protocol.login.LoginRequest
import org.openrs2.protocol.login.LoginResponse
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Provider
@ -75,6 +77,7 @@ public class LoginChannelHandler @Inject constructor(
private fun handleInitCrossDomainConnection(ctx: ChannelHandlerContext) {
ctx.pipeline().addLast(
IdleStateHandler(true, Http.TIMEOUT_SECS, Http.TIMEOUT_SECS, Http.TIMEOUT_SECS, TimeUnit.SECONDS),
HttpRequestDecoder(),
HttpResponseEncoder(),
HttpObjectAggregator(Http.MAX_CONTENT_LENGTH),

Loading…
Cancel
Save