All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.fireflysource.net.http.server.impl.Http1ServerRequestHandler.kt Maven / Gradle / Ivy
package com.fireflysource.net.http.server.impl
import com.fireflysource.common.codec.base64.Base64Utils
import com.fireflysource.common.io.toBuffer
import com.fireflysource.common.sys.SystemLogger
import com.fireflysource.net.http.client.impl.HttpProtocolNegotiator
import com.fireflysource.net.http.common.exception.BadMessageException
import com.fireflysource.net.http.common.model.*
import com.fireflysource.net.http.common.v1.decoder.HttpParser
import com.fireflysource.net.http.common.v2.decoder.SettingsBodyParser
import com.fireflysource.net.http.common.v2.frame.SettingsFrame
import com.fireflysource.net.http.server.HttpServerConnection
import com.fireflysource.net.http.server.RoutingContext
import com.fireflysource.net.http.server.impl.router.AsyncRoutingContext
import com.fireflysource.net.websocket.common.impl.AsyncWebSocketConnection
import com.fireflysource.net.websocket.common.model.AcceptHash
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.future.await
import kotlinx.coroutines.launch
import java.nio.ByteBuffer
class Http1ServerRequestHandler(private val connection: Http1ServerConnection) : HttpParser.RequestHandler {
companion object {
private val log = SystemLogger.create(Http1ServerRequestHandler::class.java)
}
var connectionListener: HttpServerConnection.Listener = HttpServerConnection.EMPTY_LISTENER
private val parserChannel: Channel = Channel(Channel.UNLIMITED)
private var expectUpgradeHttp2 = false
private var settingsFrame: SettingsFrame? = null
private var expectUpgradeWebsocket = false
init {
handleParserMessageJob()
}
private fun handleParserMessageJob() = connection.coroutineScope.launch {
var request: MetaData.Request? = null
var context: AsyncRoutingContext? = null
parserLoop@ while (true) {
val message = parserChannel.receive()
try {
when (message) {
is StartRequest -> request = newRequest(message)
is ParsedHeader -> addHeader(request, message)
is HeaderComplete -> context = newContextAndNotifyHeaderComplete(request)
is Content -> acceptContent(context, message)
is ContentComplete -> closeContentHandler(context)
is MessageComplete -> notifyHttpRequestComplete(context)
is BadMessage -> notifyException(request, context, message.exception)
is EarlyEOF -> notifyException(request, context, IllegalStateException("Parser early EOF"))
is EndRequestHandler -> {
log.info { "Exit the server request handler. id: ${connection.id}" }
break@parserLoop
}
}
} catch (e: Exception) {
notifyException(request, context, e)
}
}
}
private fun newRequest(message: StartRequest): MetaData.Request {
return MetaData.Request(message.method, HttpURI(message.uri), message.version, HttpFields())
}
private fun addHeader(request: MetaData.Request?, message: ParsedHeader) {
requireNotNull(request)
request.fields.add(message.field)
}
private suspend fun newContextAndNotifyHeaderComplete(request: MetaData.Request?): AsyncRoutingContext {
requireNotNull(request)
val httpServerRequest = AsyncHttpServerRequest(request, connection.config)
this.expectUpgradeHttp2 = HttpProtocolNegotiator.expectUpgradeHttp2(httpServerRequest)
if (expectUpgradeHttp2) {
val settingsBody = Base64Utils.decodeFromUrlSafeString(request.fields[HttpHeader.HTTP2_SETTINGS])
val settings = SettingsBodyParser.parseBody(ByteBuffer.wrap(settingsBody))
this.settingsFrame = settings
this.expectUpgradeHttp2 = settings != null
}
this.expectUpgradeWebsocket = HttpProtocolNegotiator.expectUpgradeWebsocket(httpServerRequest)
val ctx = newContext(httpServerRequest)
notifyHeaderComplete(ctx)
return ctx
}
private fun newContext(request: MetaData.Request?): AsyncRoutingContext? {
return if (request != null) {
val httpServerRequest = AsyncHttpServerRequest(request, connection.config)
newContext(httpServerRequest)
} else null
}
private fun newContext(request: AsyncHttpServerRequest): AsyncRoutingContext {
val expect100 = request.httpFields.expectServerAcceptsContent()
val closeConnection = request.httpFields.isCloseConnection(request.httpVersion)
return AsyncRoutingContext(
request,
Http1ServerResponse(connection, expect100, closeConnection),
connection
)
}
private suspend fun notifyHeaderComplete(context: RoutingContext) {
connectionListener.onHeaderComplete(context).await()
}
private fun acceptContent(context: AsyncRoutingContext?, message: Content) {
requireNotNull(context)
context.request.contentHandler.accept(message.byteBuffer, context)
}
private suspend fun closeContentHandler(context: AsyncRoutingContext?) {
requireNotNull(context)
context.request.contentHandler.closeAsync().await()
}
private suspend fun notifyHttpRequestComplete(context: RoutingContext?) {
requireNotNull(context)
context.request.isRequestComplete = true
when {
isHttpTunnel(context) -> {
val accept = connectionListener.onAcceptHttpTunnel(context.request).await()
if (accept) {
endHttpParser()
switchHttpTunnel(context)
endResponseHandler()
log.info { "Establish HTTP tunnel success. id: ${connection.id}" }
} else {
refuseHttpTunnelRequest(context)
}
}
isUpgradeHttp2(context) -> {
endHttpParser()
endResponseHandler()
switchToHttp2(context)
log.info { "Upgrade to HTTP2 success. id: ${connection.id}" }
}
isUpgradeWebsocket(context) -> {
endHttpParser()
endResponseHandler()
switchToWebSocket(context)
log.info { "Upgrade to Websocket success. id: ${connection.id}" }
}
else -> {
connection.parseNextRequest()
connectionListener.onHttpRequestComplete(context).await()
}
}
log.debug { "HTTP1 server handles request success. id: ${connection.id}" }
}
private suspend fun endHttpParser() {
parserChannel.offer(EndRequestHandler)
connection.endHttpParser()
log.info { "Upgrade protocol success. Exit HTTP1 parser. id: ${connection.id}" }
}
private suspend fun endResponseHandler() {
connection.endResponseHandler()
}
private fun isHttpTunnel(ctx: RoutingContext): Boolean {
return HttpMethod.CONNECT.`is`(ctx.method) && !connection.isSecureConnection
}
private suspend fun switchHttpTunnel(ctx: RoutingContext) {
connectionListener.onAcceptHttpTunnelHandshakeResponse(ctx).await()
connectionListener.onHttpTunnelHandshakeComplete(connection.tcpConnection)
}
private suspend fun refuseHttpTunnelRequest(ctx: RoutingContext) {
connectionListener.onRefuseHttpTunnelHandshakeResponse(ctx).await()
}
private suspend fun switchToHttp2(ctx: RoutingContext) {
val settings = settingsFrame
requireNotNull(settings)
writeHttp2UpgradeResponse()
val http2ServerConnection = createHttp2Connection()
val stream = http2ServerConnection.upgradeHttp2(settings)
val response = Http2ServerResponse(http2ServerConnection, stream)
val http2Context = AsyncRoutingContext(
ctx.request,
response,
http2ServerConnection
)
connectionListener.onHttpRequestComplete(http2Context).await()
}
private fun isUpgradeWebsocket(context: RoutingContext) =
expectUpgradeWebsocket && !context.response.isCommitted
private fun isUpgradeHttp2(context: RoutingContext) =
expectUpgradeHttp2 && !context.response.isCommitted
private suspend fun switchToWebSocket(ctx: RoutingContext) {
val handler = connectionListener.onWebSocketHandshake(ctx).await()
val clientKey = ctx.httpFields[HttpHeader.SEC_WEBSOCKET_KEY]
val serverAccept = AcceptHash.hashKey(clientKey)
val clientExtensions = ctx.httpFields.getValuesList(HttpHeader.SEC_WEBSOCKET_EXTENSIONS)
val serverExtensions = handler.extensionSelector.select(clientExtensions)
val clientSubProtocols = ctx.httpFields.getValuesList(HttpHeader.SEC_WEBSOCKET_SUBPROTOCOL)
val serverSubProtocols = handler.subProtocolSelector.select(clientSubProtocols)
val message = buildString {
append("HTTP/1.1 101 Switching Protocols\r\n")
append("Connection: Upgrade\r\n")
append("Upgrade: websocket\r\n")
append("${HttpHeader.SEC_WEBSOCKET_ACCEPT.value}: ${serverAccept}\r\n")
if (!serverExtensions.isNullOrEmpty()) {
append("${HttpHeader.SEC_WEBSOCKET_EXTENSIONS.value}: ${serverExtensions.joinToString(", ")}\r\n")
}
if (!serverSubProtocols.isNullOrEmpty()) {
append("${HttpHeader.SEC_WEBSOCKET_SUBPROTOCOL.value}: ${serverSubProtocols.joinToString(", ")}\r\n")
}
append("\r\n")
}.toBuffer()
connection.tcpConnection.writeAndFlush(message).await()
log.info { "Server response 101 Switching Protocols. upgrade: websocket, id: ${connection.id}" }
val webSocketConnection = AsyncWebSocketConnection(
connection.tcpConnection,
handler.policy,
handler.url,
serverExtensions ?: listOf(),
AsyncWebSocketConnection.defaultExtensionFactory,
serverSubProtocols ?: listOf()
)
webSocketConnection.setWebSocketMessageHandler(handler.messageHandler)
webSocketConnection.begin()
handler.connectionListener.accept(webSocketConnection).await()
}
private fun createHttp2Connection() =
Http2ServerConnection(connection.config, connection.tcpConnection)
.also { it.setListener(connectionListener).begin() }
private suspend fun writeHttp2UpgradeResponse() {
val message = ("HTTP/1.1 101 Switching Protocols\r\n" +
"Connection: Upgrade\r\n" +
"Upgrade: h2c\r\n\r\n").toBuffer()
connection.tcpConnection.writeAndFlush(message).await()
log.info { "Server response 101 Switching Protocols. upgrade: h2c, id: ${connection.id}" }
}
private suspend fun notifyException(request: MetaData.Request?, context: RoutingContext?, exception: Throwable) {
val ctx = context ?: newContext(request)
try {
log.error(exception) { "HTTP1 server parser exception. id: ${connection.id}" }
connectionListener.onException(ctx, exception).await()
} catch (e: Exception) {
log.error(e) { "HTTP1 server handles exception failure. id: ${connection.id}" }
}
when {
exception is BadMessageException -> closeConnection()
ctx == null -> closeConnection()
!ctx.request.isRequestComplete -> connection.parseNextRequest()
}
}
private suspend fun closeConnection() {
endHttpParser()
endResponseHandler()
connection.closeAsync()
}
override fun startRequest(method: String, uri: String, version: HttpVersion): Boolean {
parserChannel.offer(StartRequest(method, uri, version))
return false
}
override fun getHeaderCacheSize(): Int = 4096
override fun parsedHeader(field: HttpField) {
parserChannel.offer(ParsedHeader(field))
}
override fun headerComplete(): Boolean {
parserChannel.offer(HeaderComplete)
return false
}
override fun content(byteBuffer: ByteBuffer): Boolean {
parserChannel.offer(Content(byteBuffer))
return false
}
override fun contentComplete(): Boolean {
parserChannel.offer(ContentComplete)
return false
}
override fun messageComplete(): Boolean {
parserChannel.offer(MessageComplete)
return true
}
override fun earlyEOF() {
parserChannel.offer(EarlyEOF)
}
override fun badMessage(failure: BadMessageException) {
parserChannel.offer(BadMessage(failure))
}
}
sealed class ParserMessage
data class StartRequest(val method: String, val uri: String, val version: HttpVersion) : ParserMessage()
data class ParsedHeader(val field: HttpField) : ParserMessage()
object HeaderComplete : ParserMessage()
class Content(val byteBuffer: ByteBuffer) : ParserMessage()
object ContentComplete : ParserMessage()
object MessageComplete : ParserMessage()
object EarlyEOF : ParserMessage()
class BadMessage(val exception: Exception) : ParserMessage()
object EndRequestHandler : ParserMessage()