All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.http4s.blaze.server.Http1ServerParser.scala Maven / Gradle / Ivy

There is a newer version: 1.0.0-M41
Show newest version
/*
 * Copyright 2014 http4s.org
 *
 * 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 org.http4s
package blaze.server

import cats.effect._
import cats.syntax.all._
import org.log4s.Logger
import org.typelevel.vault._

import java.nio.ByteBuffer
import scala.collection.mutable.ListBuffer
import scala.util.Either

private[http4s] final class Http1ServerParser[F[_]](
    logger: Logger,
    maxRequestLine: Int,
    maxHeadersLen: Int,
)(implicit F: Async[F])
    extends blaze.http.parser.Http1ServerParser(maxRequestLine, maxHeadersLen, 2 * 1024) {
  private var uri: String = _
  private var method: String = _
  private var minor: Int = -1
  private val headers = new ListBuffer[Header.ToRaw]

  def minorVersion(): Int = minor

  def doParseRequestLine(buff: ByteBuffer): Boolean = parseRequestLine(buff)

  def doParseHeaders(buff: ByteBuffer): Boolean = parseHeaders(buff)

  def doParseContent(buff: ByteBuffer): Option[ByteBuffer] = Option(parseContent(buff))

  def collectMessage(
      entity: Entity[F],
      attrs: Vault,
  ): Either[(ParseFailure, HttpVersion), Request[F]] = {
    val h = Headers(headers.result())
    headers.clear()
    val protocol = if (minorVersion() == 1) HttpVersion.`HTTP/1.1` else HttpVersion.`HTTP/1.0`

    val attrsWithTrailers =
      if (minorVersion() == 1 && isChunked)
        attrs.insert(
          Message.Keys.TrailerHeaders[F],
          F.defer[Headers] {
            if (!contentComplete())
              F.raiseError(
                new IllegalStateException(
                  "Attempted to collect trailers before the body was complete."
                )
              )
            else F.pure(Headers(headers.result()))
          },
        )
      else attrs // Won't have trailers without a chunked body

    Method
      .fromString(this.method)
      .flatMap { method =>
        Uri.requestTarget(this.uri).map { uri =>
          Request(method, uri, protocol, h, entity, attrsWithTrailers)
        }
      }
      .leftMap(_ -> protocol)
  }

  override def submitRequestLine(
      methodString: String,
      uri: String,
      scheme: String,
      majorversion: Int,
      minorversion: Int,
  ): Boolean = {
    logger.trace(s"Received request($methodString $uri $scheme/$majorversion.$minorversion)")
    this.uri = uri
    this.method = methodString
    this.minor = minorversion
    false
  }

  // ///////////////// Stateful methods for the HTTP parser ///////////////////
  override protected def headerComplete(name: String, value: String): Boolean = {
    logger.trace(s"Received header '$name: $value'")
    headers += name -> value
    false
  }

  override def reset(): Unit = {
    uri = null
    method = null
    minor = -1
    headers.clear()
    super.reset()
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy