!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 scala.reflect.{ classTag, ClassTag }
|
||||||
import akka.parboiled2.CharUtils
|
import akka.parboiled2.CharUtils
|
||||||
import akka.stream.Materializer
|
import akka.stream.Materializer
|
||||||
import akka.util.ByteString
|
import akka.util.{HashCode, ByteString}
|
||||||
import akka.http.impl.util._
|
import akka.http.impl.util._
|
||||||
import akka.http.javadsl.{ model ⇒ jm }
|
import akka.http.javadsl.{ model ⇒ jm }
|
||||||
import akka.http.scaladsl.util.FastFuture._
|
import akka.http.scaladsl.util.FastFuture._
|
||||||
|
|
@ -134,11 +134,14 @@ object HttpMessage {
|
||||||
/**
|
/**
|
||||||
* The immutable model HTTP request model.
|
* The immutable model HTTP request model.
|
||||||
*/
|
*/
|
||||||
final case class HttpRequest(method: HttpMethod = HttpMethods.GET,
|
final class HttpRequest(
|
||||||
uri: Uri = Uri./,
|
val method: HttpMethod,
|
||||||
headers: immutable.Seq[HttpHeader] = Nil,
|
val uri: Uri,
|
||||||
entity: RequestEntity = HttpEntity.Empty,
|
val headers: immutable.Seq[HttpHeader],
|
||||||
protocol: HttpProtocol = HttpProtocols.`HTTP/1.1`) extends jm.HttpRequest with HttpMessage {
|
val entity: RequestEntity,
|
||||||
|
val protocol: HttpProtocol)
|
||||||
|
extends jm.HttpRequest with HttpMessage {
|
||||||
|
|
||||||
HttpRequest.verifyUri(uri)
|
HttpRequest.verifyUri(uri)
|
||||||
require(entity.isKnownEmpty || method.isEntityAccepted, s"Requests with method '${method.value}' must have an empty entity")
|
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],
|
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
|
override def getUri: jm.Uri = uri.asJava
|
||||||
/** Java API */
|
/** Java API */
|
||||||
override def withUri(uri: jm.Uri): HttpRequest = copy(uri = uri.asScala)
|
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 {
|
object HttpRequest {
|
||||||
|
|
@ -239,15 +282,28 @@ object HttpRequest {
|
||||||
case _ ⇒ throw new IllegalArgumentException("""`uri` must have scheme "http", "https" or no scheme""")
|
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.
|
* The immutable HTTP response model.
|
||||||
*/
|
*/
|
||||||
final case class HttpResponse(status: StatusCode = StatusCodes.OK,
|
final class HttpResponse(
|
||||||
headers: immutable.Seq[HttpHeader] = Nil,
|
val status: StatusCode,
|
||||||
entity: ResponseEntity = HttpEntity.Empty,
|
val headers: immutable.Seq[HttpHeader],
|
||||||
protocol: HttpProtocol = HttpProtocols.`HTTP/1.1`) extends jm.HttpResponse with HttpMessage {
|
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(entity.isKnownEmpty || status.allowsEntity, "Responses with this status code must have an empty entity")
|
||||||
require(protocol == HttpProtocols.`HTTP/1.1` || !entity.isInstanceOf[HttpEntity.Chunked],
|
require(protocol == HttpProtocols.`HTTP/1.1` || !entity.isInstanceOf[HttpEntity.Chunked],
|
||||||
"HTTP/1.0 responses must not have a chunked entity")
|
"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)
|
override def withEntity(entity: jm.RequestEntity): HttpResponse = withEntity(entity: jm.ResponseEntity)
|
||||||
|
|
||||||
def mapEntity(f: ResponseEntity ⇒ ResponseEntity): HttpResponse = withEntity(f(entity))
|
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")
|
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 = {
|
def loadSystemProperties(fileName: String): Unit = {
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
val file = new File(fileName)
|
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.RequestEntity.withoutSizeLimit"),
|
||||||
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.http.scaladsl.model.UniversalEntity.withoutSizeLimit"),
|
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.http.scaladsl.model.UniversalEntity.withoutSizeLimit"),
|
||||||
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.http.scaladsl.model.ResponseEntity.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
|
// #20014 should have been final always
|
||||||
ProblemFilters.exclude[FinalClassProblem]("akka.http.scaladsl.model.EntityStreamSizeException"),
|
ProblemFilters.exclude[FinalClassProblem]("akka.http.scaladsl.model.EntityStreamSizeException"),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue