From 367b3067c0754588b9f28fc79cb554d0f575de6b Mon Sep 17 00:00:00 2001 From: tursom Date: Thu, 21 Nov 2019 13:47:23 +0800 Subject: [PATCH] =?UTF-8?q?Http=E5=8A=9F=E8=83=BD=E5=A2=9E=E5=BC=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/tursom/web/netty/NettyHttpContent.kt | 322 +++++++++--------- .../main/kotlin/cn/tursom/web/HttpContent.kt | 22 ++ 2 files changed, 183 insertions(+), 161 deletions(-) diff --git a/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyHttpContent.kt b/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyHttpContent.kt index f34e902..30941f0 100644 --- a/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyHttpContent.kt +++ b/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyHttpContent.kt @@ -20,181 +20,181 @@ import kotlin.collections.set @Suppress("MemberVisibilityCanBePrivate", "unused") open class NettyHttpContent( - val ctx: ChannelHandlerContext, - val msg: FullHttpRequest + val ctx: ChannelHandlerContext, + val msg: FullHttpRequest ) : AdvanceHttpContent { - override val uri: String by lazy { - var uri = msg.uri() - while (uri.contains("//")) { - uri = uri.replace("//", "/") - } - uri + override val uri: String by lazy { + var uri = msg.uri() + while (uri.contains("//")) { + uri = uri.replace("//", "/") } - override val clientIp get() = ctx.channel().remoteAddress()!! - override val realIp: String = super.realIp - val httpMethod: HttpMethod get() = msg.method() - val protocolVersion: HttpVersion get() = msg.protocolVersion() - val headers: HttpHeaders get() = msg.headers() - protected val paramMap by lazy { RequestParser.parse(msg) } - override val cookieMap by lazy { getHeader("Cookie")?.let { decodeCookie(it) } ?: mapOf() } - override val body = msg.content()?.let { NettyByteBuffer(it) } + uri + } + override val clientIp get() = ctx.channel().remoteAddress()!! + override val realIp: String = super.realIp + val httpMethod: HttpMethod get() = msg.method() + val protocolVersion: HttpVersion get() = msg.protocolVersion() + val headers: HttpHeaders get() = msg.headers() + protected val paramMap by lazy { RequestParser.parse(msg) } + override val cookieMap by lazy { getHeader("Cookie")?.let { decodeCookie(it) } ?: mapOf() } + override val body = msg.content()?.let { NettyByteBuffer(it) } - val responseMap = HashMap() - val responseListMap = HashMap>() - override val responseBody = ByteArrayOutputStream() - override var responseCode: Int = 200 - override var responseMessage: String? = null - override val method: String get() = httpMethod.name() - val chunkedList = ArrayList() + val responseMap = HashMap() + val responseListMap = HashMap>() + override val responseBody = ByteArrayOutputStream() + override var responseCode: Int = 200 + override var responseMessage: String? = null + override val method: String get() = httpMethod.name() + val chunkedList = ArrayList() - override fun getHeader(header: String): String? { - return headers[header] + override fun getHeader(header: String): String? { + return headers[header] + } + + override fun getHeaders(): List> { + return headers.toList() + } + + override fun getParam(param: String): String? { + return paramMap[param]?.get(0) + } + + override fun getParams(): Map> { + return paramMap + } + + override fun getParams(param: String): List? { + return paramMap[param] + } + + override fun addParam(key: String, value: String) { + if (!paramMap.containsKey(key)) { + paramMap[key] = ArrayList() + } + (paramMap[key] as ArrayList).add(value) + } + + override fun setResponseHeader(name: String, value: Any) { + responseMap[name] = value + } + + override fun addResponseHeader(name: String, value: Any) { + val list = responseListMap[name] ?: run { + val newList = ArrayList() + responseListMap[name] = newList + newList + } + list.add(value) + } + + override fun write(message: String) { + responseBody.write(message.toByteArray()) + } + + override fun write(byte: Byte) { + responseBody.write(byte.toInt()) + } + + override fun write(bytes: ByteArray, offset: Int, size: Int) { + responseBody.write(bytes, offset, size) + } + + override fun write(buffer: ByteBuffer) { + buffer.writeTo(responseBody) + } + + override fun reset() { + responseBody.reset() + } + + override fun finish() { + finish(responseBody.buf, 0, responseBody.size()) + } + + override fun finish(buffer: ByteArray, offset: Int, size: Int) { + finish(Unpooled.wrappedBuffer(buffer, offset, size)) + } + + override fun finish(buffer: ByteBuffer) { + if (buffer is NettyByteBuffer) { + finish(buffer.byteBuf) + } else { + super.finish(buffer) + } + } + + fun finish(buf: ByteBuf) = finish(buf, HttpResponseStatus.valueOf(responseCode)) + fun finish(buf: ByteBuf, responseCode: HttpResponseStatus) { + val response = DefaultFullHttpResponse(HttpVersion.HTTP_1_1, responseCode, buf) + finish(response) + } + + fun finish(response: FullHttpResponse) { + val heads = response.headers() + addHeaders( + heads, mapOf( + HttpHeaderNames.CONTENT_TYPE to "${HttpHeaderValues.TEXT_PLAIN}; charset=UTF-8", + HttpHeaderNames.CONTENT_LENGTH to response.content().readableBytes(), + HttpHeaderNames.CONNECTION to HttpHeaderValues.KEEP_ALIVE + ) + ) + + ctx.writeAndFlush(response) + } + + fun addHeaders(heads: HttpHeaders, defaultHeaders: Map) { + responseListMap.forEach { (t, u) -> + u.forEach { + heads.add(t, it) + } } - override fun getHeaders(): List> { - return headers.toList() + defaultHeaders.forEach { (t, u) -> + heads.set(t, u) } - override fun getParam(param: String): String? { - return paramMap[param]?.get(0) + responseMap.forEach { (t, u) -> + heads.set(t, u) } + } - override fun getParams(): Map> { - return paramMap - } + override fun writeChunkedHeader() { + val response = DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK) + response.status = if (responseMessage != null) HttpResponseStatus(responseCode, responseMessage) + else HttpResponseStatus.valueOf(responseCode) + val heads = response.headers() + addHeaders( + heads, mapOf( + HttpHeaderNames.CONTENT_TYPE to "${HttpHeaderValues.TEXT_PLAIN}; charset=UTF-8", + HttpHeaderNames.CONNECTION to HttpHeaderValues.KEEP_ALIVE, + HttpHeaderNames.TRANSFER_ENCODING to "chunked" + ) + ) + ctx.write(response) + } - override fun getParams(param: String): List? { - return paramMap[param] - } + override fun addChunked(buffer: ByteBuffer) { + chunkedList.add(buffer) + } - override fun addParam(key: String, value: String) { - if (!paramMap.containsKey(key)) { - paramMap[key] = ArrayList() - } - (paramMap[key] as ArrayList).add(value) - } + override fun finishChunked() { + val httpChunkWriter = HttpChunkedInput(NettyChunkedByteBuffer(chunkedList)) + ctx.writeAndFlush(httpChunkWriter) + } - override fun setResponseHeader(name: String, value: Any) { - responseMap[name] = value - } + override fun finishChunked(chunked: Chunked) { + val httpChunkWriter = HttpChunkedInput(NettyChunkedInput(chunked)) + ctx.writeAndFlush(httpChunkWriter) + } - override fun addResponseHeader(name: String, value: Any) { - val list = responseListMap[name] ?: run { - val newList = ArrayList() - responseListMap[name] = newList - newList - } - list.add(value) - } + override fun finishFile(file: File, chunkSize: Int) { + writeChunkedHeader() + ctx.writeAndFlush(HttpChunkedInput(ChunkedFile(file, chunkSize))) + } - override fun write(message: String) { - responseBody.write(message.toByteArray()) - } - - override fun write(byte: Byte) { - responseBody.write(byte.toInt()) - } - - override fun write(bytes: ByteArray, offset: Int, size: Int) { - responseBody.write(bytes, offset, size) - } - - override fun write(buffer: ByteBuffer) { - buffer.writeTo(responseBody) - } - - override fun reset() { - responseBody.reset() - } - - override fun finish() { - finish(responseBody.buf, 0, responseBody.size()) - } - - override fun finish(buffer: ByteArray, offset: Int, size: Int) { - finish(Unpooled.wrappedBuffer(buffer, offset, size)) - } - - override fun finish(buffer: ByteBuffer) { - if (buffer is NettyByteBuffer) { - finish(buffer.byteBuf) - } else { - super.finish(buffer) - } - } - - fun finish(buf: ByteBuf) = finish(buf, HttpResponseStatus.valueOf(responseCode)) - fun finish(buf: ByteBuf, responseCode: HttpResponseStatus) { - val response = DefaultFullHttpResponse(HttpVersion.HTTP_1_1, responseCode, buf) - finish(response) - } - - fun finish(response: FullHttpResponse) { - val heads = response.headers() - addHeaders( - heads, mapOf( - HttpHeaderNames.CONTENT_TYPE to "${HttpHeaderValues.TEXT_PLAIN}; charset=UTF-8", - HttpHeaderNames.CONTENT_LENGTH to response.content().readableBytes(), - HttpHeaderNames.CONNECTION to HttpHeaderValues.KEEP_ALIVE - ) - ) - - ctx.writeAndFlush(response) - } - - fun addHeaders(heads: HttpHeaders, defaultHeaders: Map) { - responseListMap.forEach { (t, u) -> - u.forEach { - heads.add(t, it) - } - } - - defaultHeaders.forEach { (t, u) -> - heads.set(t, u) - } - - responseMap.forEach { (t, u) -> - heads.set(t, u) - } - } - - override fun writeChunkedHeader() { - val response = DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK) - response.status = if (responseMessage != null) HttpResponseStatus(responseCode, responseMessage) - else HttpResponseStatus.valueOf(responseCode) - val heads = response.headers() - addHeaders( - heads, mapOf( - HttpHeaderNames.CONTENT_TYPE to "${HttpHeaderValues.TEXT_PLAIN}; charset=UTF-8", - HttpHeaderNames.CONNECTION to HttpHeaderValues.KEEP_ALIVE, - HttpHeaderNames.TRANSFER_ENCODING to "chunked" - ) - ) - ctx.write(response) - } - - override fun addChunked(buffer: ByteBuffer) { - chunkedList.add(buffer) - } - - override fun finishChunked() { - val httpChunkWriter = HttpChunkedInput(NettyChunkedByteBuffer(chunkedList)) - ctx.writeAndFlush(httpChunkWriter) - } - - override fun finishChunked(chunked: Chunked) { - val httpChunkWriter = HttpChunkedInput(NettyChunkedInput(chunked)) - ctx.writeAndFlush(httpChunkWriter) - } - - override fun finishFile(file: File, chunkSize: Int) { - writeChunkedHeader() - ctx.writeAndFlush(HttpChunkedInput(ChunkedFile(file, chunkSize))) - } - - override fun finishFile(file: RandomAccessFile, offset: Long, length: Long, chunkSize: Int) { - writeChunkedHeader() - ctx.writeAndFlush(HttpChunkedInput(ChunkedFile(file, offset, length, chunkSize))) - } + override fun finishFile(file: RandomAccessFile, offset: Long, length: Long, chunkSize: Int) { + writeChunkedHeader() + ctx.writeAndFlush(HttpChunkedInput(ChunkedFile(file, offset, length, chunkSize))) + } } diff --git a/web/src/main/kotlin/cn/tursom/web/HttpContent.kt b/web/src/main/kotlin/cn/tursom/web/HttpContent.kt index 78c0a51..c4ec416 100644 --- a/web/src/main/kotlin/cn/tursom/web/HttpContent.kt +++ b/web/src/main/kotlin/cn/tursom/web/HttpContent.kt @@ -174,4 +174,26 @@ interface HttpContent { ) fun setContextType(type: Any) = setResponseHeader("Content-Type", type) + + fun jump(url: String) = temporaryMoved(url) + fun moved(url: String) = permanentlyMoved(url) + + fun permanentlyMoved(url: String) { + setResponseHeader("Location", url) + finish(301) + } + + fun temporaryMoved(url: String) { + noStore() + setResponseHeader("Location", url) + finish(302) + } + + fun noCache() { + setResponseHeader("Cache-Control", "no-cache") + } + + fun noStore() { + setResponseHeader("Cache-Control", "no-store") + } } \ No newline at end of file