!htc WIP #19956 removing case classes from HttpMessages
This commit is contained in:
parent
b3c691e4a8
commit
c69efe4dd8
5 changed files with 228 additions and 13 deletions
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* Copyright (C) 2015-2016 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package akka.http
|
||||
|
||||
import akka.http.scaladsl.model._
|
||||
import java.util.concurrent.TimeUnit
|
||||
import org.openjdk.jmh.annotations._
|
||||
|
||||
/**
|
||||
* Benchmark used to verify move to name-based extraction does not hurt preformance.
|
||||
* It does not allocate an Option thus it should be more optimal actually.
|
||||
*/
|
||||
@State(Scope.Benchmark)
|
||||
@OutputTimeUnit(TimeUnit.MICROSECONDS)
|
||||
@BenchmarkMode(Array(Mode.Throughput))
|
||||
class HttpMessageMatchingBenchmark {
|
||||
|
||||
val req = HttpRequest()
|
||||
val res = HttpResponse()
|
||||
|
||||
@Benchmark
|
||||
def res_matching: HttpResponse = {
|
||||
res match {
|
||||
case r @ HttpResponse(status, headers, entity, protocol) => r
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
def req_matching: HttpRequest = {
|
||||
req match {
|
||||
case r @ HttpRequest(method, uri, headers, entity, protocol) => r
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ import scala.collection.immutable
|
|||
import scala.reflect.{ classTag, ClassTag }
|
||||
import akka.parboiled2.CharUtils
|
||||
import akka.stream.Materializer
|
||||
import akka.util.ByteString
|
||||
import akka.util.{HashCode, ByteString}
|
||||
import akka.http.impl.util._
|
||||
import akka.http.javadsl.{ model ⇒ jm }
|
||||
import akka.http.scaladsl.util.FastFuture._
|
||||
|
|
@ -134,11 +134,14 @@ object HttpMessage {
|
|||
/**
|
||||
* The immutable model HTTP request model.
|
||||
*/
|
||||
final case class HttpRequest(method: HttpMethod = HttpMethods.GET,
|
||||
uri: Uri = Uri./,
|
||||
headers: immutable.Seq[HttpHeader] = Nil,
|
||||
entity: RequestEntity = HttpEntity.Empty,
|
||||
protocol: HttpProtocol = HttpProtocols.`HTTP/1.1`) extends jm.HttpRequest with HttpMessage {
|
||||
final class HttpRequest(
|
||||
val method: HttpMethod,
|
||||
val uri: Uri,
|
||||
val headers: immutable.Seq[HttpHeader],
|
||||
val entity: RequestEntity,
|
||||
val protocol: HttpProtocol)
|
||||
extends jm.HttpRequest with HttpMessage {
|
||||
|
||||
HttpRequest.verifyUri(uri)
|
||||
require(entity.isKnownEmpty || method.isEntityAccepted, s"Requests with method '${method.value}' must have an empty entity")
|
||||
require(protocol != HttpProtocols.`HTTP/1.0` || !entity.isInstanceOf[HttpEntity.Chunked],
|
||||
|
|
@ -196,6 +199,46 @@ final case class HttpRequest(method: HttpMethod = HttpMethods.GET,
|
|||
override def getUri: jm.Uri = uri.asJava
|
||||
/** Java API */
|
||||
override def withUri(uri: jm.Uri): HttpRequest = copy(uri = uri.asScala)
|
||||
|
||||
/* Manual Case Class things, to easen bin-compat */
|
||||
|
||||
def copy(method: HttpMethod = method,
|
||||
uri: Uri = uri,
|
||||
headers: immutable.Seq[HttpHeader] = headers,
|
||||
entity: RequestEntity = entity,
|
||||
protocol: HttpProtocol = protocol) = new HttpRequest(method, uri, headers, entity, protocol)
|
||||
|
||||
|
||||
|
||||
override def hashCode(): Int = {
|
||||
var result = HashCode.SEED
|
||||
result = HashCode.hash(result, _1)
|
||||
result = HashCode.hash(result, _2)
|
||||
result = HashCode.hash(result, _3)
|
||||
result = HashCode.hash(result, _4)
|
||||
result = HashCode.hash(result, _5)
|
||||
result
|
||||
}
|
||||
|
||||
override def equals(obj: scala.Any): Boolean = obj match {
|
||||
case HttpRequest(_method, _uri, _headers, _entity, _protocol) =>
|
||||
method == _method &&
|
||||
uri == _uri &&
|
||||
headers == _headers &&
|
||||
entity == _entity &&
|
||||
protocol == _protocol
|
||||
case _ => false
|
||||
}
|
||||
|
||||
override def toString = s"""HttpRequest(${_1},${_2},${_3},${_4},${_5})"""
|
||||
|
||||
// name-based unapply accessors
|
||||
def _1 = method
|
||||
def _2 = uri
|
||||
def _3 = headers
|
||||
def _4 = entity
|
||||
def _5 = protocol
|
||||
|
||||
}
|
||||
|
||||
object HttpRequest {
|
||||
|
|
@ -239,15 +282,28 @@ object HttpRequest {
|
|||
case _ ⇒ throw new IllegalArgumentException("""`uri` must have scheme "http", "https" or no scheme""")
|
||||
}
|
||||
}
|
||||
|
||||
/* Manual Case Class things, to easen bin-compat */
|
||||
|
||||
def apply(method: HttpMethod = HttpMethods.GET,
|
||||
uri: Uri = Uri./,
|
||||
headers: immutable.Seq[HttpHeader] = Nil,
|
||||
entity: RequestEntity = HttpEntity.Empty,
|
||||
protocol: HttpProtocol = HttpProtocols.`HTTP/1.1`) = new HttpRequest(method, uri, headers, entity, protocol)
|
||||
|
||||
def unapply(any: HttpRequest) = new OptHttpRequest(any)
|
||||
}
|
||||
|
||||
/**
|
||||
* The immutable HTTP response model.
|
||||
*/
|
||||
final case class HttpResponse(status: StatusCode = StatusCodes.OK,
|
||||
headers: immutable.Seq[HttpHeader] = Nil,
|
||||
entity: ResponseEntity = HttpEntity.Empty,
|
||||
protocol: HttpProtocol = HttpProtocols.`HTTP/1.1`) extends jm.HttpResponse with HttpMessage {
|
||||
final class HttpResponse(
|
||||
val status: StatusCode,
|
||||
val headers: immutable.Seq[HttpHeader],
|
||||
val entity: ResponseEntity,
|
||||
val protocol: HttpProtocol)
|
||||
extends jm.HttpResponse with HttpMessage {
|
||||
|
||||
require(entity.isKnownEmpty || status.allowsEntity, "Responses with this status code must have an empty entity")
|
||||
require(protocol == HttpProtocols.`HTTP/1.1` || !entity.isInstanceOf[HttpEntity.Chunked],
|
||||
"HTTP/1.0 responses must not have a chunked entity")
|
||||
|
|
@ -272,4 +328,58 @@ final case class HttpResponse(status: StatusCode = StatusCodes.OK,
|
|||
override def withEntity(entity: jm.RequestEntity): HttpResponse = withEntity(entity: jm.ResponseEntity)
|
||||
|
||||
def mapEntity(f: ResponseEntity ⇒ ResponseEntity): HttpResponse = withEntity(f(entity))
|
||||
|
||||
/* Manual Case Class things, to easen bin-compat */
|
||||
|
||||
def copy(status: StatusCode = status,
|
||||
headers: immutable.Seq[HttpHeader] = headers,
|
||||
entity: ResponseEntity = entity,
|
||||
protocol: HttpProtocol = protocol) = new HttpResponse(status, headers, entity, protocol)
|
||||
|
||||
|
||||
override def equals(obj: scala.Any): Boolean = obj match {
|
||||
case HttpResponse(_status, _headers, _entity, _protocol) =>
|
||||
status == _status &&
|
||||
headers == _headers &&
|
||||
entity == _entity &&
|
||||
protocol == _protocol
|
||||
case _ => false
|
||||
}
|
||||
|
||||
override def hashCode: Int = {
|
||||
var result = HashCode.SEED
|
||||
result = HashCode.hash(result, _1)
|
||||
result = HashCode.hash(result, _2)
|
||||
result = HashCode.hash(result, _3)
|
||||
result = HashCode.hash(result, _4)
|
||||
result
|
||||
}
|
||||
|
||||
override def toString = s"""HttpResponse(${_1},${_2},${_3},${_4})"""
|
||||
|
||||
// name-based unapply accessors
|
||||
def _1 = this.status
|
||||
def _2 = this.headers
|
||||
def _3 = this.entity
|
||||
def _4 = this.protocol
|
||||
|
||||
}
|
||||
|
||||
object HttpResponse {
|
||||
/* Manual Case Class things, to easen bin-compat */
|
||||
|
||||
def apply(status: StatusCode = StatusCodes.OK,
|
||||
headers: immutable.Seq[HttpHeader] = Nil,
|
||||
entity: ResponseEntity = HttpEntity.Empty,
|
||||
protocol: HttpProtocol = HttpProtocols.`HTTP/1.1`) = new HttpResponse(status, headers, entity, protocol)
|
||||
|
||||
def unapply(any: HttpResponse): OptHttpResponse = new OptHttpResponse(any)
|
||||
}
|
||||
|
||||
final class OptHttpRequest(val get: HttpRequest) extends AnyVal {
|
||||
def isEmpty: Boolean = get == null
|
||||
}
|
||||
|
||||
final class OptHttpResponse(val get: HttpResponse) extends AnyVal {
|
||||
def isEmpty: Boolean = get == null
|
||||
}
|
||||
|
|
@ -532,6 +532,47 @@ object AkkaBuild extends Build {
|
|||
javacOptions in doc ++= Seq("-Xdoclint:none")
|
||||
)
|
||||
|
||||
def akkaPreviousArtifacts(id: String): Def.Initialize[Set[sbt.ModuleID]] = Def.setting {
|
||||
if (enableMiMa) {
|
||||
val versions = {
|
||||
val akka23Versions = Seq("2.3.11", "2.3.12", "2.3.13", "2.3.14")
|
||||
val akka24Versions = Seq("2.4.0", "2.4.1", "2.4.2")
|
||||
val akka24NewArtifacts = Seq(
|
||||
"akka-cluster-sharding",
|
||||
"akka-cluster-tools",
|
||||
"akka-cluster-metrics",
|
||||
"akka-persistence",
|
||||
"akka-distributed-data-experimental",
|
||||
"akka-persistence-query-experimental"
|
||||
)
|
||||
scalaBinaryVersion.value match {
|
||||
case "2.11" if !akka24NewArtifacts.contains(id) => akka23Versions ++ akka24Versions
|
||||
case _ => akka24Versions // Only Akka 2.4.x for scala > than 2.11
|
||||
}
|
||||
}
|
||||
|
||||
// check against all binary compatible artifacts
|
||||
versions.map(organization.value %% id % _).toSet
|
||||
}
|
||||
else Set.empty
|
||||
}
|
||||
|
||||
def akkaStreamAndHttpPreviousArtifacts(id: String): Def.Initialize[Set[sbt.ModuleID]] = Def.setting {
|
||||
if (enableMiMa) {
|
||||
val versions = {
|
||||
val akka24Versions = Seq("2.4.2")
|
||||
val akka24NewArtifacts = Seq(
|
||||
"akka-http-core"
|
||||
)
|
||||
|
||||
akka24Versions
|
||||
}
|
||||
|
||||
// check against all binary compatible artifacts
|
||||
versions.map(organization.value %% id % _).toSet
|
||||
} else Set.empty
|
||||
}
|
||||
|
||||
def loadSystemProperties(fileName: String): Unit = {
|
||||
import scala.collection.JavaConverters._
|
||||
val file = new File(fileName)
|
||||
|
|
|
|||
|
|
@ -675,6 +675,33 @@ object MiMa extends AutoPlugin {
|
|||
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.http.scaladsl.model.RequestEntity.withoutSizeLimit"),
|
||||
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.http.scaladsl.model.UniversalEntity.withoutSizeLimit"),
|
||||
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.http.scaladsl.model.ResponseEntity.withoutSizeLimit"),
|
||||
// #19956 Remove exposed case classes in HTTP model
|
||||
ProblemFilters.exclude[MissingTypesProblem]("akka.http.scaladsl.model.HttpRequest$"),
|
||||
ProblemFilters.exclude[IncompatibleResultTypeProblem]("akka.http.scaladsl.model.HttpRequest.unapply"), // returned Option[HttpRequest], now returns HttpRequest – no Option allocations!
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpRequest.<init>$default$1"),
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpRequest.<init>$default$2"),
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpRequest.<init>$default$3"),
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpRequest.<init>$default$4"),
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpRequest.<init>$default$5"),
|
||||
ProblemFilters.exclude[MissingTypesProblem]("akka.http.scaladsl.model.HttpResponse"), // was a case class (Serializable, Product, Equals)
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpResponse.productElement"),
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpResponse.productArity"),
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpResponse.canEqual"),
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpResponse.productIterator"),
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpResponse.productPrefix"),
|
||||
|
||||
ProblemFilters.exclude[MissingTypesProblem]("akka.http.scaladsl.model.HttpRequest"),
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpRequest.productElement"),
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpRequest.productArity"),
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpRequest.canEqual"),
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpRequest.productIterator"),
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpRequest.productPrefix"),
|
||||
ProblemFilters.exclude[MissingTypesProblem]("akka.http.scaladsl.model.HttpResponse$"),
|
||||
ProblemFilters.exclude[IncompatibleResultTypeProblem]("akka.http.scaladsl.model.HttpResponse.unapply"), // returned Option[HttpRequest], now returns HttpRequest – no Option allocations!
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpResponse.<init>$default$1"),
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpResponse.<init>$default$2"),
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpResponse.<init>$default$3"),
|
||||
ProblemFilters.exclude[MissingMethodProblem]("akka.http.scaladsl.model.HttpResponse.<init>$default$4"),
|
||||
|
||||
// #20014 should have been final always
|
||||
ProblemFilters.exclude[FinalClassProblem]("akka.http.scaladsl.model.EntityStreamSizeException"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue