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.HttpMethod
import io.netty.handler.codec.http.HttpRequest import io.netty.handler.codec.http.HttpRequest
import io.netty.handler.codec.http.HttpResponseStatus import io.netty.handler.codec.http.HttpResponseStatus
import io.netty.handler.timeout.IdleStateEvent
import org.openrs2.buffer.use import org.openrs2.buffer.use
import org.openrs2.game.net.http.Http import org.openrs2.game.net.http.Http
@ -39,5 +40,21 @@ public object CrossDomainChannelHandler : SimpleChannelInboundHandler<HttpReques
override fun channelReadComplete(ctx: ChannelHandlerContext) { override fun channelReadComplete(ctx: ChannelHandlerContext) {
ctx.flush() 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.HttpRequest
import io.netty.handler.codec.http.HttpResponseStatus import io.netty.handler.codec.http.HttpResponseStatus
import io.netty.handler.codec.http.HttpVersion import io.netty.handler.codec.http.HttpVersion
import io.netty.handler.codec.http.LastHttpContent
import io.netty.util.ReferenceCounted import io.netty.util.ReferenceCounted
import org.openrs2.buffer.copiedBuffer import org.openrs2.buffer.copiedBuffer
import org.openrs2.buffer.use import org.openrs2.buffer.use
public object Http { public object Http {
public const val TIMEOUT_SECS: Long = 30
public const val MAX_CONTENT_LENGTH: Int = 65536 public const val MAX_CONTENT_LENGTH: Int = 65536
public const val TEXT_X_CROSS_DOMAIN_POLICY: String = "text/x-cross-domain-policy" public const val TEXT_X_CROSS_DOMAIN_POLICY: String = "text/x-cross-domain-policy"
private const val BANNER = "OpenRS2" 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( private fun writeResponse(
ctx: ChannelHandlerContext, ctx: ChannelHandlerContext,
request: HttpRequest, request: HttpRequest,
@ -34,17 +41,32 @@ public object Http {
HttpVersion.HTTP_1_1 HttpVersion.HTTP_1_1
} }
val keepAlive = isKeepAlive(request, version)
val response = DefaultHttpResponse(version, status) 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.SERVER, BANNER)
response.headers().add(HttpHeaderNames.CONTENT_TYPE, contentType) response.headers().add(HttpHeaderNames.CONTENT_TYPE, contentType)
response.headers().add(HttpHeaderNames.CONTENT_LENGTH, contentLength) 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(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.HttpHeaderValues
import io.netty.handler.codec.http.HttpRequest import io.netty.handler.codec.http.HttpRequest
import io.netty.handler.codec.http.HttpResponseStatus import io.netty.handler.codec.http.HttpResponseStatus
import io.netty.handler.timeout.IdleStateEvent
import org.openrs2.buffer.use import org.openrs2.buffer.use
import org.openrs2.game.net.FileProvider import org.openrs2.game.net.FileProvider
import javax.inject.Inject import javax.inject.Inject
@ -39,5 +40,21 @@ public class HttpChannelHandler @Inject constructor(
override fun channelReadComplete(ctx: ChannelHandlerContext) { override fun channelReadComplete(ctx: ChannelHandlerContext) {
ctx.flush() 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.HttpObjectAggregator
import io.netty.handler.codec.http.HttpRequestDecoder import io.netty.handler.codec.http.HttpRequestDecoder
import io.netty.handler.codec.http.HttpResponseEncoder 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.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -14,6 +16,7 @@ public class HttpChannelInitializer @Inject constructor(
) : ChannelInitializer<Channel>() { ) : ChannelInitializer<Channel>() {
override fun initChannel(ch: Channel) { override fun initChannel(ch: Channel) {
ch.pipeline().addLast( ch.pipeline().addLast(
IdleStateHandler(true, Http.TIMEOUT_SECS, Http.TIMEOUT_SECS, Http.TIMEOUT_SECS, TimeUnit.SECONDS),
HttpRequestDecoder(), HttpRequestDecoder(),
HttpResponseEncoder(), HttpResponseEncoder(),
HttpObjectAggregator(Http.MAX_CONTENT_LENGTH), 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.HttpRequestDecoder
import io.netty.handler.codec.http.HttpResponseEncoder import io.netty.handler.codec.http.HttpResponseEncoder
import io.netty.handler.codec.string.StringDecoder import io.netty.handler.codec.string.StringDecoder
import io.netty.handler.timeout.IdleStateHandler
import org.openrs2.buffer.copiedBuffer import org.openrs2.buffer.copiedBuffer
import org.openrs2.game.net.crossdomain.CrossDomainChannelHandler import org.openrs2.game.net.crossdomain.CrossDomainChannelHandler
import org.openrs2.game.net.http.Http 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.js5.XorDecoder
import org.openrs2.protocol.login.LoginRequest import org.openrs2.protocol.login.LoginRequest
import org.openrs2.protocol.login.LoginResponse import org.openrs2.protocol.login.LoginResponse
import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Provider import javax.inject.Provider
@ -75,6 +77,7 @@ public class LoginChannelHandler @Inject constructor(
private fun handleInitCrossDomainConnection(ctx: ChannelHandlerContext) { private fun handleInitCrossDomainConnection(ctx: ChannelHandlerContext) {
ctx.pipeline().addLast( ctx.pipeline().addLast(
IdleStateHandler(true, Http.TIMEOUT_SECS, Http.TIMEOUT_SECS, Http.TIMEOUT_SECS, TimeUnit.SECONDS),
HttpRequestDecoder(), HttpRequestDecoder(),
HttpResponseEncoder(), HttpResponseEncoder(),
HttpObjectAggregator(Http.MAX_CONTENT_LENGTH), HttpObjectAggregator(Http.MAX_CONTENT_LENGTH),

Loading…
Cancel
Save