zio.http.netty.model.Conversions.scala Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2021 - 2023 Sporta Technologies PVT LTD & the ZIO HTTP contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package zio.http.netty.model
import scala.collection.AbstractIterator
import zio.http.Server.Config.CompressionOptions
import zio.http._
import com.aayushatharva.brotli4j.encoder.Encoder
import io.netty.handler.codec.compression.StandardCompressionOptions
import io.netty.handler.codec.http._
import io.netty.handler.codec.http.websocketx.WebSocketScheme
private[netty] object Conversions {
def methodFromNetty(method: HttpMethod): Method =
method match {
case HttpMethod.OPTIONS => Method.OPTIONS
case HttpMethod.GET => Method.GET
case HttpMethod.HEAD => Method.HEAD
case HttpMethod.POST => Method.POST
case HttpMethod.PUT => Method.PUT
case HttpMethod.PATCH => Method.PATCH
case HttpMethod.DELETE => Method.DELETE
case HttpMethod.TRACE => Method.TRACE
case HttpMethod.CONNECT => Method.CONNECT
case method => Method.CUSTOM(method.name())
}
def methodToNetty(method: Method): HttpMethod =
method match {
case Method.OPTIONS => HttpMethod.OPTIONS
case Method.GET => HttpMethod.GET
case Method.HEAD => HttpMethod.HEAD
case Method.POST => HttpMethod.POST
case Method.PUT => HttpMethod.PUT
case Method.PATCH => HttpMethod.PATCH
case Method.DELETE => HttpMethod.DELETE
case Method.TRACE => HttpMethod.TRACE
case Method.CONNECT => HttpMethod.CONNECT
case Method.ANY => HttpMethod.GET
case Method.CUSTOM(name) => new HttpMethod(name)
}
def headersToNetty(headers: Headers): HttpHeaders =
headers match {
case Headers.FromIterable(_) => encodeHeaderListToNetty(headers)
case Headers.Native(value, _, _, _) => value.asInstanceOf[HttpHeaders]
case Headers.Concat(_, _) => encodeHeaderListToNetty(headers)
case Headers.Empty => new DefaultHttpHeaders()
}
def urlToNetty(url: URL): String = {
// As per the spec, the path should contain only the relative path.
// Host and port information should be in the headers.
val url0 = if (url.path.isEmpty) url.addLeadingSlash else url
url0.relative.addLeadingSlash.encode
}
private def nettyHeadersIterator(headers: HttpHeaders): Iterator[Header] =
new AbstractIterator[Header] {
private val nettyIterator = headers.iteratorCharSequence()
override def hasNext: Boolean = nettyIterator.hasNext
override def next(): Header = {
val entry = nettyIterator.next()
Header.Custom(entry.getKey, entry.getValue)
}
}
def headersFromNetty(headers: HttpHeaders): Headers =
Headers.Native(
headers,
(headers: HttpHeaders) => nettyHeadersIterator(headers),
// NOTE: Netty's headers.get is case-insensitive
(headers: HttpHeaders, key: CharSequence) => headers.get(key),
(headers: HttpHeaders, key: CharSequence) => headers.contains(key),
)
private def encodeHeaderListToNetty(headers: Iterable[Header]): HttpHeaders = {
val nettyHeaders = new DefaultHttpHeaders()
val setCookieName = Header.SetCookie.name
val iter = headers.iterator
while (iter.hasNext) {
val header = iter.next()
val name = header.headerName
if (name == setCookieName) {
nettyHeaders.add(name, header.renderedValueAsCharSequence)
} else {
nettyHeaders.set(name, header.renderedValueAsCharSequence)
}
}
nettyHeaders
}
def statusToNetty(status: Status): HttpResponseStatus =
HttpResponseStatus.valueOf(status.code, status.reasonPhrase)
def statusFromNetty(status: HttpResponseStatus): Status =
Status.fromInt(status.code) match {
case Status.Custom(code, _) => Status.Custom(code, status.reasonPhrase)
case status => status
}
def schemeToNetty(scheme: Scheme): Option[HttpScheme] = scheme match {
case Scheme.HTTP => Option(HttpScheme.HTTP)
case Scheme.HTTPS => Option(HttpScheme.HTTPS)
case _ => None
}
def schemeToNettyWebSocketScheme(scheme: Scheme): Option[WebSocketScheme] = scheme match {
case Scheme.WS => Option(WebSocketScheme.WS)
case Scheme.WSS => Option(WebSocketScheme.WSS)
case _ => None
}
def schemeFromNetty(scheme: HttpScheme): Option[Scheme] = scheme match {
case HttpScheme.HTTPS => Option(Scheme.HTTPS)
case HttpScheme.HTTP => Option(Scheme.HTTP)
case _ => None
}
def schemeFromNetty(scheme: WebSocketScheme): Option[Scheme] = scheme match {
case WebSocketScheme.WSS => Option(Scheme.WSS)
case WebSocketScheme.WS => Option(Scheme.WS)
case _ => None
}
def compressionOptionsToNetty(
compressionOptions: CompressionOptions,
): io.netty.handler.codec.compression.CompressionOptions =
compressionOptions match {
case CompressionOptions.GZip(cfg) =>
StandardCompressionOptions.gzip(cfg.level, cfg.bits, cfg.mem)
case CompressionOptions.Deflate(cfg) =>
StandardCompressionOptions.deflate(cfg.level, cfg.bits, cfg.mem)
case CompressionOptions.Brotli(cfg) =>
StandardCompressionOptions.brotli(
new Encoder.Parameters().setQuality(cfg.quality).setWindow(cfg.lgwin).setMode(brotliModeToJava(cfg.mode)),
)
}
def brotliModeToJava(brotli: CompressionOptions.Mode): Encoder.Mode = brotli match {
case CompressionOptions.Mode.Font => Encoder.Mode.FONT
case CompressionOptions.Mode.Text => Encoder.Mode.TEXT
case CompressionOptions.Mode.Generic => Encoder.Mode.GENERIC
}
def versionToNetty(version: Version): HttpVersion = version match {
case Version.Http_1_0 => HttpVersion.HTTP_1_0
case Version.Http_1_1 => HttpVersion.HTTP_1_1
case Version.Default => HttpVersion.HTTP_1_1
}
}