Merge pull request #18667 from analytically/release-2.3-dev
=htc accept entity for DELETE and GET methods
This commit is contained in:
commit
142ffea52b
6 changed files with 66 additions and 22 deletions
|
|
@ -30,4 +30,9 @@ public abstract class HttpMethod {
|
|||
* Returns if requests with this method may contain an entity.
|
||||
*/
|
||||
public abstract boolean isEntityAccepted();
|
||||
|
||||
/**
|
||||
* Returns the entity acceptance level for this method.
|
||||
*/
|
||||
public abstract akka.http.scaladsl.model.RequestEntityAcceptance requestEntityAcceptance();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ public final class HttpMethods {
|
|||
/**
|
||||
* Create a custom method type.
|
||||
*/
|
||||
public static HttpMethod custom(String value, boolean safe, boolean idempotent, boolean entityAccepted) {
|
||||
return akka.http.scaladsl.model.HttpMethod.custom(value, safe, idempotent, entityAccepted);
|
||||
public static HttpMethod custom(String value, boolean safe, boolean idempotent, akka.http.scaladsl.model.RequestEntityAcceptance requestEntityAcceptance) {
|
||||
return akka.http.scaladsl.model.HttpMethod.custom(value, safe, idempotent, requestEntityAcceptance);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
package akka.http.impl.engine.rendering
|
||||
|
||||
import akka.http.ClientConnectionSettings
|
||||
import akka.http.scaladsl.model.RequestEntityAcceptance._
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import akka.event.LoggingAdapter
|
||||
|
|
@ -99,7 +100,7 @@ private[http] class HttpRequestRendererFactory(userAgentHeader: Option[headers.`
|
|||
}
|
||||
|
||||
def renderContentLength(contentLength: Long) =
|
||||
if (method.isEntityAccepted) r ~~ `Content-Length` ~~ contentLength ~~ CrLf else r
|
||||
if (method.isEntityAccepted && (contentLength > 0 || method.requestEntityAcceptance == Expected)) r ~~ `Content-Length` ~~ contentLength ~~ CrLf else r
|
||||
|
||||
def renderStreamed(body: Source[ByteString, Any]): RequestRenderingOutput =
|
||||
RequestRenderingOutput.Streamed(renderByteStrings(r, body))
|
||||
|
|
|
|||
|
|
@ -6,47 +6,65 @@ package akka.http.scaladsl.model
|
|||
|
||||
import akka.http.impl.util._
|
||||
import akka.http.javadsl.{ model ⇒ jm }
|
||||
import akka.http.scaladsl.model.RequestEntityAcceptance._
|
||||
|
||||
sealed trait RequestEntityAcceptance {
|
||||
def isEntityAccepted: Boolean
|
||||
}
|
||||
object RequestEntityAcceptance {
|
||||
case object Expected extends RequestEntityAcceptance {
|
||||
override def isEntityAccepted: Boolean = true
|
||||
}
|
||||
case object Tolerated extends RequestEntityAcceptance {
|
||||
override def isEntityAccepted: Boolean = true
|
||||
}
|
||||
case object Disallowed extends RequestEntityAcceptance {
|
||||
override def isEntityAccepted: Boolean = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The method of an HTTP request.
|
||||
* @param isSafe true if the resource should not be altered on the server
|
||||
* @param isIdempotent true if requests can be safely (& automatically) repeated
|
||||
* @param isEntityAccepted true if meaning of request entities is properly defined
|
||||
* @param requestEntityAcceptance Expected if meaning of request entities is properly defined
|
||||
*/
|
||||
final case class HttpMethod private[http] (override val value: String,
|
||||
isSafe: Boolean,
|
||||
isIdempotent: Boolean,
|
||||
isEntityAccepted: Boolean) extends jm.HttpMethod with SingletonValueRenderable {
|
||||
requestEntityAcceptance: RequestEntityAcceptance) extends jm.HttpMethod with SingletonValueRenderable {
|
||||
def name = value
|
||||
|
||||
override def isEntityAccepted: Boolean = requestEntityAcceptance.isEntityAccepted
|
||||
override def toString: String = s"HttpMethod($value)"
|
||||
}
|
||||
|
||||
object HttpMethod {
|
||||
def custom(name: String, safe: Boolean, idempotent: Boolean, entityAccepted: Boolean): HttpMethod = {
|
||||
def custom(name: String, safe: Boolean, idempotent: Boolean, requestEntityAcceptance: RequestEntityAcceptance): HttpMethod = {
|
||||
require(name.nonEmpty, "value must be non-empty")
|
||||
require(!safe || idempotent, "An HTTP method cannot be safe without being idempotent")
|
||||
apply(name, safe, idempotent, entityAccepted)
|
||||
apply(name, safe, idempotent, requestEntityAcceptance)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a custom method by name and assumes properties conservatively to be
|
||||
* safe = idempotent = false and entityAccepted = true.
|
||||
* safe = false, idempotent = false and requestEntityAcceptance = Expected.
|
||||
*/
|
||||
def custom(name: String): HttpMethod = custom(name, safe = false, idempotent = false, entityAccepted = true)
|
||||
def custom(name: String): HttpMethod = custom(name, safe = false, idempotent = false, requestEntityAcceptance = Expected)
|
||||
}
|
||||
|
||||
object HttpMethods extends ObjectRegistry[String, HttpMethod] {
|
||||
private def register(method: HttpMethod): HttpMethod = register(method.value, method)
|
||||
|
||||
// format: OFF
|
||||
val CONNECT = register(HttpMethod("CONNECT", isSafe = false, isIdempotent = false, isEntityAccepted = false))
|
||||
val DELETE = register(HttpMethod("DELETE" , isSafe = false, isIdempotent = true , isEntityAccepted = false))
|
||||
val GET = register(HttpMethod("GET" , isSafe = true , isIdempotent = true , isEntityAccepted = false))
|
||||
val HEAD = register(HttpMethod("HEAD" , isSafe = true , isIdempotent = true , isEntityAccepted = false))
|
||||
val OPTIONS = register(HttpMethod("OPTIONS", isSafe = true , isIdempotent = true , isEntityAccepted = true))
|
||||
val PATCH = register(HttpMethod("PATCH" , isSafe = false, isIdempotent = false, isEntityAccepted = true))
|
||||
val POST = register(HttpMethod("POST" , isSafe = false, isIdempotent = false, isEntityAccepted = true))
|
||||
val PUT = register(HttpMethod("PUT" , isSafe = false, isIdempotent = true , isEntityAccepted = true))
|
||||
val TRACE = register(HttpMethod("TRACE" , isSafe = true , isIdempotent = true , isEntityAccepted = false))
|
||||
val CONNECT = register(HttpMethod("CONNECT", isSafe = false, isIdempotent = false, requestEntityAcceptance = Disallowed))
|
||||
val DELETE = register(HttpMethod("DELETE" , isSafe = false, isIdempotent = true , requestEntityAcceptance = Tolerated))
|
||||
val GET = register(HttpMethod("GET" , isSafe = true , isIdempotent = true , requestEntityAcceptance = Tolerated))
|
||||
val HEAD = register(HttpMethod("HEAD" , isSafe = true , isIdempotent = true , requestEntityAcceptance = Disallowed))
|
||||
val OPTIONS = register(HttpMethod("OPTIONS", isSafe = true , isIdempotent = true , requestEntityAcceptance = Expected))
|
||||
val PATCH = register(HttpMethod("PATCH" , isSafe = false, isIdempotent = false, requestEntityAcceptance = Expected))
|
||||
val POST = register(HttpMethod("POST" , isSafe = false, isIdempotent = false, requestEntityAcceptance = Expected))
|
||||
val PUT = register(HttpMethod("PUT" , isSafe = false, isIdempotent = true , requestEntityAcceptance = Expected))
|
||||
val TRACE = register(HttpMethod("TRACE" , isSafe = true , isIdempotent = true , requestEntityAcceptance = Disallowed))
|
||||
// format: ON
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import akka.http.scaladsl.model.HttpEntity._
|
|||
import akka.http.scaladsl.model.HttpMethods._
|
||||
import akka.http.scaladsl.model.HttpProtocols._
|
||||
import akka.http.scaladsl.model.MediaTypes._
|
||||
import akka.http.scaladsl.model.RequestEntityAcceptance.Expected
|
||||
import akka.http.scaladsl.model.StatusCodes._
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.model.headers._
|
||||
|
|
@ -37,7 +38,7 @@ class RequestParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll {
|
|||
implicit val system = ActorSystem(getClass.getSimpleName, testConf)
|
||||
import system.dispatcher
|
||||
|
||||
val BOLT = HttpMethod.custom("BOLT", safe = false, idempotent = true, entityAccepted = true)
|
||||
val BOLT = HttpMethod.custom("BOLT", safe = false, idempotent = true, requestEntityAcceptance = Expected)
|
||||
implicit val materializer = ActorMaterializer()
|
||||
|
||||
"The request parsing logic should" - {
|
||||
|
|
@ -448,17 +449,26 @@ class RequestParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll {
|
|||
}
|
||||
}
|
||||
|
||||
"with an illegal entity" in new Test {
|
||||
"with an illegal entity using CONNECT" in new Test {
|
||||
"""CONNECT /resource/yes HTTP/1.1
|
||||
|Transfer-Encoding: chunked
|
||||
|Host: x
|
||||
|
|
||||
|""" should parseToError(422: StatusCode, ErrorInfo("CONNECT requests must not have an entity"))
|
||||
}
|
||||
"with an illegal entity using HEAD" in new Test {
|
||||
"""HEAD /resource/yes HTTP/1.1
|
||||
|Content-length: 3
|
||||
|Host: x
|
||||
|
|
||||
|foo""" should parseToError(422: StatusCode, ErrorInfo("HEAD requests must not have an entity"))
|
||||
"""DELETE /resource/yes HTTP/1.1
|
||||
}
|
||||
"with an illegal entity using TRACE" in new Test {
|
||||
"""TRACE /resource/yes HTTP/1.1
|
||||
|Transfer-Encoding: chunked
|
||||
|Host: x
|
||||
|
|
||||
|""" should parseToError(422: StatusCode, ErrorInfo("DELETE requests must not have an entity"))
|
||||
|""" should parseToError(422: StatusCode, ErrorInfo("TRACE requests must not have an entity"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,6 +124,16 @@ class RequestRendererSpec extends FreeSpec with Matchers with BeforeAndAfterAll
|
|||
|The content please!"""
|
||||
}
|
||||
}
|
||||
|
||||
"DELETE request without headers and without body" in new TestSetup() {
|
||||
HttpRequest(DELETE, "/abc") should renderTo {
|
||||
"""DELETE /abc HTTP/1.1
|
||||
|Host: test.com:8080
|
||||
|User-Agent: akka-http/1.0.0
|
||||
|
|
||||
|"""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"proper render a chunked" - {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue