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.Http2ServerOutputChannel.kt Maven / Gradle / Ivy
package com.fireflysource.net.http.server.impl
import com.fireflysource.common.io.BufferUtils
import com.fireflysource.common.io.flipToFill
import com.fireflysource.common.io.flipToFlush
import com.fireflysource.common.sys.Result
import com.fireflysource.common.sys.Result.discard
import com.fireflysource.common.sys.SystemLogger
import com.fireflysource.net.http.common.model.MetaData
import com.fireflysource.net.http.common.v2.frame.DataFrame
import com.fireflysource.net.http.common.v2.frame.Frame
import com.fireflysource.net.http.common.v2.frame.HeadersFrame
import com.fireflysource.net.http.common.v2.stream.Stream
import com.fireflysource.net.http.server.HttpServerOutputChannel
import com.fireflysource.net.tcp.buffer.DelegatedOutputBufferArray
import com.fireflysource.net.tcp.buffer.OutputBufferArray
import java.nio.ByteBuffer
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.atomic.AtomicBoolean
/**
* @author Pengtao Qiu
*/
class Http2ServerOutputChannel(
private val response: MetaData.Response,
private val stream: Stream
) : HttpServerOutputChannel {
companion object {
private val log = SystemLogger.create(Http2ServerOutputChannel::class.java)
private const val defaultMaxFrameSize = Frame.DEFAULT_MAX_LENGTH.toLong()
}
private val committed = AtomicBoolean(false)
private val closed = AtomicBoolean(false)
private val messages = LinkedList()
override fun commit(): CompletableFuture {
if (committed.compareAndSet(false, true)) {
messages.offer(HeadersOutputMessage)
}
return Result.DONE
}
override fun isCommitted(): Boolean = committed.get()
override fun write(byteBuffers: Array, offset: Int, length: Int): CompletableFuture {
val message = BuffersOutputMessage(byteBuffers, offset, length)
messages.offer(message)
writeOutputMessage()
val future = CompletableFuture()
future.complete(message.remaining())
return future
}
override fun write(byteBufferList: List, offset: Int, length: Int): CompletableFuture {
return write(byteBufferList.toTypedArray(), offset, length)
}
override fun write(string: String): CompletableFuture {
return write(string, StandardCharsets.UTF_8)
}
override fun write(string: String, charset: Charset): CompletableFuture {
val byteBuffer = BufferUtils.toBuffer(string, charset)
return write(byteBuffer)
}
override fun write(byteBuffer: ByteBuffer): CompletableFuture {
val message = BufferOutputMessage(byteBuffer)
messages.offer(message)
writeOutputMessage()
val future = CompletableFuture()
future.complete(byteBuffer.remaining())
return future
}
override fun isOpen(): Boolean = closed.get()
override fun closeAsync(): CompletableFuture {
if (closed.compareAndSet(false, true)) {
writeOutputMessage()
val trailers = response.trailerSupplier?.get()
if (trailers != null) {
val trailerMetaData = MetaData.Response(trailers)
trailerMetaData.isOnlyTrailer = true
val headersFrameTrailer = HeadersFrame(stream.id, trailerMetaData, null, true)
stream.headers(headersFrameTrailer, discard())
}
}
return Result.DONE
}
override fun close() {
closeAsync()
}
private fun writeOutputMessage() {
val message = messages.poll()
if (message != null) {
val last = messages.isEmpty() && response.trailerSupplier == null
when (message) {
is HeadersOutputMessage -> writeHeaders(last)
is BufferOutputMessage -> writeBuffer(message, last)
is BuffersOutputMessage -> writeBuffers(message, last)
}
}
}
private fun writeHeaders(last: Boolean) {
val headersFrame = HeadersFrame(stream.id, response, null, last)
stream.headers(headersFrame, discard())
}
private fun writeBuffer(message: BufferOutputMessage, last: Boolean) {
val dataFrame = DataFrame(stream.id, message.byteBuffer, last)
stream.data(dataFrame, discard())
}
private fun writeBuffers(message: BuffersOutputMessage, last: Boolean) {
val length = message.getCurrentLength()
when {
length == 1 -> {
val byteBuffer = message.byteBuffers[message.getCurrentOffset()]
val dataFrame = DataFrame(stream.id, byteBuffer, last)
stream.data(dataFrame, discard())
}
length > 1 -> {
while (message.hasRemaining()) {
val remaining = message.remaining()
val size = remaining.coerceAtMost(defaultMaxFrameSize).toInt()
val buffer = BufferUtils.allocate(size)
val pos = buffer.flipToFill()
log.debug { "HTTP2 outputs buffer array. before remaining: $remaining, size: $size" }
while (buffer.hasRemaining()) {
val offset = message.getCurrentOffset()
val src = message.byteBuffers[offset]
BufferUtils.put(src, buffer)
log.debug { "HTTP2 outputs buffer array. put offset: $offset" }
}
buffer.flipToFlush(pos)
val end = last && !message.hasRemaining()
val dataFrame = DataFrame(stream.id, buffer, end)
stream.data(dataFrame, discard())
log.debug { "HTTP2 outputs buffer array. after remaining: ${message.remaining()}, end: $end" }
}
}
}
}
}
sealed class Http2OutputMessage
object HeadersOutputMessage : Http2OutputMessage()
class BufferOutputMessage(val byteBuffer: ByteBuffer) : Http2OutputMessage()
class BuffersOutputMessage(
val byteBuffers: Array, val offset: Int, val length: Int,
private val delegatedBufferArray: DelegatedOutputBufferArray = DelegatedOutputBufferArray(
byteBuffers, offset, length, discard()
)
) : OutputBufferArray by delegatedBufferArray, Http2OutputMessage()