添加对Http Multipart的支持

This commit is contained in:
tursom 2020-05-20 00:23:45 +08:00
parent 8675eccd33
commit 7685ca42f5
11 changed files with 48 additions and 18 deletions

View File

@ -33,7 +33,7 @@ open class NettyHttpContent(
} }
uri uri
} }
override val clientIp get() = ctx.channel().remoteAddress()!! override val remoteAddress get() = ctx.channel().remoteAddress()!!
override val realIp: String = super.realIp override val realIp: String = super.realIp
val httpMethod: HttpMethod get() = request.method() val httpMethod: HttpMethod get() = request.method()
val protocolVersion: HttpVersion get() = request.protocolVersion() val protocolVersion: HttpVersion get() = request.protocolVersion()
@ -71,6 +71,8 @@ open class NettyHttpContent(
} }
} }
override fun peekBody(): ByteBuffer? = bodyList.peek()?.content()?.let { NettyByteBuffer(it.slice()) }
override fun waitBody(action: (end: Boolean) -> Unit) { override fun waitBody(action: (end: Boolean) -> Unit) {
if (!requestSendFully) { if (!requestSendFully) {
waitBodyHandler.add(action) waitBodyHandler.add(action)

View File

@ -19,7 +19,7 @@ class NettyHttpHandler(
override fun channelRead0(ctx: ChannelHandlerContext, msg: FullHttpRequest) { override fun channelRead0(ctx: ChannelHandlerContext, msg: FullHttpRequest) {
val handlerContext = NettyHttpContent(ctx, msg) val handlerContext = NettyHttpContent(ctx, msg)
log?.debug("{} {} {}", handlerContext.clientIp, handlerContext.method, handlerContext.uri) log?.debug("{} {} {}", handlerContext.remoteAddress, handlerContext.method, handlerContext.uri)
handler.handle(handlerContext) handler.handle(handlerContext)
} }

View File

@ -27,7 +27,7 @@ class NettyHttpServer(
var bodySize: Int = 512 * 1024, var bodySize: Int = 512 * 1024,
autoRun: Boolean = false, autoRun: Boolean = false,
var webSocketPath: Iterable<Pair<String, WebSocketHandler<NettyWebSocketContent>>> = listOf(), var webSocketPath: Iterable<Pair<String, WebSocketHandler<NettyWebSocketContent>>> = listOf(),
var readTimeout: Int? = null, var readTimeout: Int? = 60,
var writeTimeout: Int? = null, var writeTimeout: Int? = null,
decodeType: NettyHttpDecodeType = NettyHttpDecodeType.MULTI_PART, decodeType: NettyHttpDecodeType = NettyHttpDecodeType.MULTI_PART,
backlog: Int = 1024 backlog: Int = 1024
@ -37,7 +37,7 @@ class NettyHttpServer(
bodySize: Int = 512 * 1024, bodySize: Int = 512 * 1024,
autoRun: Boolean = false, autoRun: Boolean = false,
webSocketPath: Iterable<Pair<String, WebSocketHandler<NettyWebSocketContent>>> = listOf(), webSocketPath: Iterable<Pair<String, WebSocketHandler<NettyWebSocketContent>>> = listOf(),
readTimeout: Int? = null, readTimeout: Int? = 60,
writeTimeout: Int? = null, writeTimeout: Int? = null,
decodeType: NettyHttpDecodeType = NettyHttpDecodeType.MULTI_PART, decodeType: NettyHttpDecodeType = NettyHttpDecodeType.MULTI_PART,
handler: (content: NettyHttpContent) -> Unit handler: (content: NettyHttpContent) -> Unit
@ -49,7 +49,6 @@ class NettyHttpServer(
bodySize, autoRun, webSocketPath, readTimeout, writeTimeout, decodeType bodySize, autoRun, webSocketPath, readTimeout, writeTimeout, decodeType
) )
var decodeType: NettyHttpDecodeType = decodeType var decodeType: NettyHttpDecodeType = decodeType
set(value) { set(value) {
if (value != field) { if (value != field) {

View File

@ -8,10 +8,12 @@ import io.netty.buffer.Unpooled
import io.netty.channel.Channel import io.netty.channel.Channel
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame import io.netty.handler.codec.http.websocketx.TextWebSocketFrame
import java.net.SocketAddress
class NettyWebSocketContent( class NettyWebSocketContent(
private val channel: Channel val channel: Channel
) : WebSocketContent { ) : WebSocketContent {
override val remoteAddress: SocketAddress get() = channel.remoteAddress()
override fun writeText(buffer: ByteBuffer) { override fun writeText(buffer: ByteBuffer) {
if (buffer is NettyByteBuffer) { if (buffer is NettyByteBuffer) {
channel.writeAndFlush(TextWebSocketFrame(buffer.byteBuf)) channel.writeAndFlush(TextWebSocketFrame(buffer.byteBuf))

View File

@ -15,13 +15,14 @@ interface HttpContent : ResponseHeaderAdapter, RequestHeaderAdapter {
var responseCode: Int var responseCode: Int
var responseMessage: String? var responseMessage: String?
val body: ByteBuffer? val body: ByteBuffer?
val clientIp: SocketAddress val remoteAddress: SocketAddress
val method: String val method: String
val realIp val realIp
get() = getHeader("X-Forwarded-For") ?: clientIp.toString().let { str -> get() = getHeader("X-Forwarded-For") ?: remoteAddress.toString().let { str ->
str.substring(1, str.indexOf(':').let { if (it < 1) str.length else it - 1 }) str.substring(1, str.indexOf(':').let { if (it < 1) str.length else it - 1 })
} }
fun peekBody(): ByteBuffer?
fun waitBody(action: (end: Boolean) -> Unit = { addBodyParam() }) fun waitBody(action: (end: Boolean) -> Unit = { addBodyParam() })
fun addBodyParam(body: ByteBuffer) fun addBodyParam(body: ByteBuffer)
fun addBodyParam() { fun addBodyParam() {

View File

@ -2,9 +2,11 @@ package cn.tursom.web
import cn.tursom.core.buffer.ByteBuffer import cn.tursom.core.buffer.ByteBuffer
import cn.tursom.core.buffer.impl.HeapByteBuffer import cn.tursom.core.buffer.impl.HeapByteBuffer
import java.net.SocketAddress
import java.nio.charset.Charset import java.nio.charset.Charset
interface WebSocketContent { interface WebSocketContent {
val remoteAddress: SocketAddress
fun writeText(buffer: ByteBuffer) fun writeText(buffer: ByteBuffer)
fun writeText(bytes: ByteArray) = writeText(HeapByteBuffer(bytes)) fun writeText(bytes: ByteArray) = writeText(HeapByteBuffer(bytes))
fun writeText(str: String, charset: Charset = Charsets.UTF_8) = writeText(str.toByteArray(charset)) fun writeText(str: String, charset: Charset = Charsets.UTF_8) = writeText(str.toByteArray(charset))

View File

@ -1,7 +1,6 @@
package cn.tursom.web.router package cn.tursom.web.router
import cn.tursom.core.buffer.ByteBuffer import cn.tursom.core.buffer.ByteBuffer
import cn.tursom.core.cast
import cn.tursom.core.lambda import cn.tursom.core.lambda
import cn.tursom.core.regex.regex import cn.tursom.core.regex.regex
import cn.tursom.json.JsonWorkerImpl import cn.tursom.json.JsonWorkerImpl
@ -282,13 +281,13 @@ open class RoutedHttpHandler(
fun autoReturn(method: Method, result: Any?, content: HttpContent, doLog: Boolean? = null) { fun autoReturn(method: Method, result: Any?, content: HttpContent, doLog: Boolean? = null) {
method.getAnnotation(ContextType::class.java)?.let { method.getAnnotation(ContextType::class.java)?.let {
content.setContextType(it.type.value) content.setContextType(it.type.value)
log?.debug("{}: autoReturn context type auto set to {}({})", content.clientIp, it.type.key, it.type.value) log?.debug("{}: autoReturn context type auto set to {}({})", content.remoteAddress, it.type.key, it.type.value)
} }
autoReturn(result, content, doLog ?: method.doLog) autoReturn(result, content, doLog ?: method.doLog)
} }
fun autoReturn(result: Any?, content: HttpContent, doLog: Boolean = true) { fun autoReturn(result: Any?, content: HttpContent, doLog: Boolean = true) {
if (doLog) log?.debug("{}: autoReturn: {}", content.clientIp, result) if (doLog) log?.debug("{}: autoReturn: {}", content.remoteAddress, result)
result ?: return result ?: return
when (result) { when (result) {
null -> content.finish(404) null -> content.finish(404)
@ -310,7 +309,7 @@ open class RoutedHttpHandler(
} }
fun finishHtml(result: Any?, content: HttpContent, doLog: Boolean = true) { fun finishHtml(result: Any?, content: HttpContent, doLog: Boolean = true) {
if (doLog) log?.debug("{} finishHtml {}", content.clientIp, result) if (doLog) log?.debug("{} finishHtml {}", content.remoteAddress, result)
result ?: return result ?: return
when (result) { when (result) {
null -> content.finish(404) null -> content.finish(404)
@ -328,7 +327,7 @@ open class RoutedHttpHandler(
} }
fun finishText(result: Any?, content: HttpContent, doLog: Boolean = true) { fun finishText(result: Any?, content: HttpContent, doLog: Boolean = true) {
if (doLog) log?.debug("{} finishText {}", content.clientIp, result) if (doLog) log?.debug("{} finishText {}", content.remoteAddress, result)
result ?: return result ?: return
when (result) { when (result) {
null -> content.finish(404) null -> content.finish(404)
@ -346,7 +345,7 @@ open class RoutedHttpHandler(
} }
fun finishJson(result: Any?, content: HttpContent, doLog: Boolean = true) { fun finishJson(result: Any?, content: HttpContent, doLog: Boolean = true) {
if (doLog) log?.debug("{} finishJson {}", content.clientIp, result) if (doLog) log?.debug("{} finishJson {}", content.remoteAddress, result)
result ?: return result ?: return
when (result) { when (result) {
null -> content.finish(404) null -> content.finish(404)
@ -362,7 +361,7 @@ open class RoutedHttpHandler(
} }
else -> { else -> {
val json = json?.toJson(result) val json = json?.toJson(result)
if (doLog) log?.debug("{} finishJson: generate json {}", content.clientIp, json) if (doLog) log?.debug("{} finishJson: generate json {}", content.remoteAddress, json)
if (json != null) { if (json != null) {
content.finishJson(json.toByteArray()) content.finishJson(json.toByteArray())
} else { } else {

View File

@ -14,7 +14,7 @@ class EmptyHttpContent(
override var responseCode: Int = 200, override var responseCode: Int = 200,
override var responseMessage: String? = null, override var responseMessage: String? = null,
override val body: ByteBuffer? = null, override val body: ByteBuffer? = null,
override val clientIp: SocketAddress = InetSocketAddress(0), override val remoteAddress: SocketAddress = InetSocketAddress(0),
override val method: String = "GET", override val method: String = "GET",
override val cookieMap: Map<String, String> = mapOf(), override val cookieMap: Map<String, String> = mapOf(),
override val requestSendFully: Boolean override val requestSendFully: Boolean
@ -46,5 +46,6 @@ class EmptyHttpContent(
override fun finishFile(file: RandomAccessFile, offset: Long, length: Long, chunkSize: Int) {} override fun finishFile(file: RandomAccessFile, offset: Long, length: Long, chunkSize: Int) {}
override fun addBodyParam(body: ByteBuffer) {} override fun addBodyParam(body: ByteBuffer) {}
override fun waitBody(action: (end: Boolean) -> Unit) {} override fun waitBody(action: (end: Boolean) -> Unit) {}
override fun peekBody(): ByteBuffer? = null
} }

View File

@ -2,6 +2,6 @@ dependencies {
implementation project(":web") implementation project(":web")
api project(":json") api project(":json")
api group: 'org.slf4j', name: 'slf4j-api', version: '1.7.29' api group: 'org.slf4j', name: 'slf4j-api', version: '1.7.29'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1' compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
compile group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlinVersion compile group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlinVersion
} }

View File

@ -0,0 +1,24 @@
package cn.tursom.web
import cn.tursom.core.buffer.ByteBuffer
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
suspend fun HttpContent.getBody(): ByteBuffer {
suspendCoroutine<Boolean> { cont ->
waitBody {
cont.resume(it)
}
}
return body!!
}
suspend fun HttpContent.waitBodyParam(): HttpContent {
suspendCoroutine<Boolean> { cont ->
waitBody {
addBodyParam()
cont.resume(it)
}
}
return this
}

View File

@ -223,7 +223,7 @@ open class AsyncRoutedHttpHandler(
fun autoReturn(method: KCallable<*>, result: Any?, content: HttpContent, doLog: Boolean? = null) { fun autoReturn(method: KCallable<*>, result: Any?, content: HttpContent, doLog: Boolean? = null) {
method.findAnnotation<ContextType>()?.let { method.findAnnotation<ContextType>()?.let {
content.setContextType(it.type.value) content.setContextType(it.type.value)
log?.debug("{}: autoReturn context type auto set to {}({})", content.clientIp, it.type.key, it.type.value) log?.debug("{}: autoReturn context type auto set to {}({})", content.remoteAddress, it.type.key, it.type.value)
} }
autoReturn(result, content, doLog ?: method.doLog) autoReturn(result, content, doLog ?: method.doLog)
} }