-
Notifications
You must be signed in to change notification settings - Fork 412
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor: Add Response Handler (#727)
* added response handler that is responsible of transofrming internal zio-http Response in netty HTTP response. * added response handler that is responsible of transforming internal zio-http Response in netty HTTP response. * removed redundant code * merged and fixed code. * formatting * simplified the channelRead0 implementation avoiding patter matching on status. Updated comments * simplified more the code * formatting * more formatting
- Loading branch information
1 parent
83792d1
commit 2f0eb65
Showing
6 changed files
with
114 additions
and
132 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletions
89
zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package zhttp.service.server.content.handlers | ||
|
||
import io.netty.buffer.ByteBuf | ||
import io.netty.channel.ChannelHandler.Sharable | ||
import io.netty.channel.{ChannelHandlerContext, DefaultFileRegion, SimpleChannelInboundHandler} | ||
import io.netty.handler.codec.http._ | ||
import zhttp.http.{HttpData, Response} | ||
import zhttp.service.server.ServerTimeGenerator | ||
import zhttp.service.{ChannelFuture, HttpRuntime, Server} | ||
import zio.stream.ZStream | ||
import zio.{UIO, ZIO} | ||
|
||
import java.io.File | ||
|
||
@Sharable | ||
private[zhttp] case class ServerResponseHandler[R]( | ||
runtime: HttpRuntime[R], | ||
config: Server.Config[R, Throwable], | ||
serverTime: ServerTimeGenerator, | ||
) extends SimpleChannelInboundHandler[Response](false) { | ||
|
||
type Ctx = ChannelHandlerContext | ||
|
||
override def channelRead0(ctx: Ctx, response: Response): Unit = { | ||
implicit val iCtx: ChannelHandlerContext = ctx | ||
|
||
ctx.write(encodeResponse(response)) | ||
response.data match { | ||
case HttpData.BinaryStream(stream) => runtime.unsafeRun(ctx) { writeStreamContent(stream) } | ||
case HttpData.File(file) => unsafeWriteFileContent(file) | ||
case _ => ctx.flush() | ||
} | ||
() | ||
} | ||
|
||
override def exceptionCaught(ctx: Ctx, cause: Throwable): Unit = { | ||
config.error.fold(super.exceptionCaught(ctx, cause))(f => runtime.unsafeRun(ctx)(f(cause))) | ||
} | ||
|
||
/** | ||
* Checks if an encoded version of the response exists, uses it if it does. Otherwise, it will return a fresh | ||
* response. It will also set the server time if requested by the client. | ||
*/ | ||
private def encodeResponse(res: Response): HttpResponse = { | ||
|
||
val jResponse = res.attribute.encoded match { | ||
|
||
// Check if the encoded response exists and/or was modified. | ||
case Some((oRes, jResponse)) if oRes eq res => | ||
jResponse match { | ||
// Duplicate the response without allocating much memory | ||
case response: FullHttpResponse => response.retainedDuplicate() | ||
|
||
case response => response | ||
} | ||
|
||
case _ => res.unsafeEncode() | ||
} | ||
// Identify if the server time should be set and update if required. | ||
if (res.attribute.serverTime) jResponse.headers().set(HttpHeaderNames.DATE, serverTime.refreshAndGet()) | ||
jResponse | ||
} | ||
|
||
/** | ||
* Writes Binary Stream data to the Channel | ||
*/ | ||
private def writeStreamContent[A]( | ||
stream: ZStream[R, Throwable, ByteBuf], | ||
)(implicit ctx: Ctx): ZIO[R, Throwable, Unit] = { | ||
for { | ||
_ <- stream.foreach(c => UIO(ctx.writeAndFlush(c))) | ||
_ <- ChannelFuture.unit(ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT)) | ||
} yield () | ||
} | ||
|
||
/** | ||
* Writes file content to the Channel. Does not use Chunked transfer encoding | ||
*/ | ||
private def unsafeWriteFileContent(file: File)(implicit ctx: ChannelHandlerContext): Unit = { | ||
import java.io.RandomAccessFile | ||
|
||
val raf = new RandomAccessFile(file, "r") | ||
val fileLength = raf.length() | ||
// Write the content. | ||
ctx.write(new DefaultFileRegion(raf.getChannel, 0, fileLength)) | ||
// Write the end marker. | ||
ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT): Unit | ||
} | ||
} |