Skip to content

Commit c0ffee4

Browse files
committed
!hco #15915 introduce more shades of HttpEntities
The introduction of BodyParts again showed that not all entity types are useful for every kind of context. There are now these contexts where HttpEntities are used: - requests - responses - body parts And several kinds of entities: - Strict - Default - Chunked - CloseDelimited - IndefiniteLength To increase type safety of the API marker-interfaces are introduced defining which kinds of entities can be used in which contexts: - RequestEntity: Strict, Default, Chunked - ResponseEntity: Strict, Default, Chunked, CloseDelimited - BodyPartEntity: Strict, Default, IndefiniteLength Also, to be able still to provide abstractions over some kinds of entities additional auxiliary interfaces were necessary: - MessageEntity = RequestEntity >: ResponseEntity: Strict, Default, Chunked (type alias for RequestEntity) - UniversalEntity = RequestEntity with ResponseEntity with BodyPartEntity = Strict, Default
1 parent 6f68f87 commit c0ffee4

40 files changed

+289
-159
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
3+
*/
4+
5+
package akka.http.model.japi;
6+
7+
/** Marker-interface for entity types that can be used in a body part */
8+
public interface BodyPartEntity extends HttpEntity {}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
3+
*/
4+
5+
package akka.http.model.japi;
6+
7+
import java.io.File;
8+
9+
import akka.util.ByteString;
10+
import org.reactivestreams.Publisher;
11+
12+
import akka.stream.FlowMaterializer;
13+
import akka.http.model.HttpEntity$;
14+
15+
/** Constructors for HttpEntity instances */
16+
public final class HttpEntities {
17+
private HttpEntities() {}
18+
19+
public static HttpEntityStrict create(String string) {
20+
return HttpEntity$.MODULE$.apply(string);
21+
}
22+
23+
public static HttpEntityStrict create(byte[] bytes) {
24+
return HttpEntity$.MODULE$.apply(bytes);
25+
}
26+
27+
public static HttpEntityStrict create(ByteString bytes) {
28+
return HttpEntity$.MODULE$.apply(bytes);
29+
}
30+
31+
public static HttpEntityStrict create(ContentType contentType, String string) {
32+
return HttpEntity$.MODULE$.apply((akka.http.model.ContentType) contentType, string);
33+
}
34+
35+
public static HttpEntityStrict create(ContentType contentType, byte[] bytes) {
36+
return HttpEntity$.MODULE$.apply((akka.http.model.ContentType) contentType, bytes);
37+
}
38+
39+
public static HttpEntityStrict create(ContentType contentType, ByteString bytes) {
40+
return HttpEntity$.MODULE$.apply((akka.http.model.ContentType) contentType, bytes);
41+
}
42+
43+
public static UniversalEntity create(ContentType contentType, File file) {
44+
return HttpEntity$.MODULE$.apply((akka.http.model.ContentType) contentType, file);
45+
}
46+
47+
public static HttpEntityDefault create(ContentType contentType, long contentLength, Publisher<ByteString> data) {
48+
return new akka.http.model.HttpEntity.Default((akka.http.model.ContentType) contentType, contentLength, data);
49+
}
50+
51+
public static HttpEntityCloseDelimited createCloseDelimited(ContentType contentType, Publisher<ByteString> data) {
52+
return new akka.http.model.HttpEntity.CloseDelimited((akka.http.model.ContentType) contentType, data);
53+
}
54+
55+
public static HttpEntityIndefiniteLength createIndefiniteLength(ContentType contentType, Publisher<ByteString> data) {
56+
return new akka.http.model.HttpEntity.IndefiniteLength((akka.http.model.ContentType) contentType, data);
57+
}
58+
59+
public static HttpEntityChunked createChunked(ContentType contentType, Publisher<ChunkStreamPart> chunks) {
60+
return new akka.http.model.HttpEntity.Chunked(
61+
(akka.http.model.ContentType) contentType,
62+
Util.<ChunkStreamPart, akka.http.model.HttpEntity.ChunkStreamPart>upcastPublisher(chunks));
63+
}
64+
65+
public static HttpEntityChunked createChunked(ContentType contentType, Publisher<ByteString> data, FlowMaterializer materializer) {
66+
return akka.http.model.HttpEntity.Chunked$.MODULE$.fromData(
67+
(akka.http.model.ContentType) contentType,
68+
data, materializer);
69+
}
70+
}

akka-http-core/src/main/java/akka/http/model/japi/HttpEntity.java

Lines changed: 17 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
import akka.util.ByteString;
1010
import org.reactivestreams.Publisher;
1111

12-
import java.io.File;
13-
1412
/**
1513
* Represents the entity of an Http message. An entity consists of the content-type of the data
1614
* and the actual data itself. Some subtypes of HttpEntity also define the content-length of the
@@ -19,16 +17,24 @@
1917
* An HttpEntity can be of several kinds:
2018
*
2119
* - HttpEntity.Empty: the statically known empty entity
20+
* - HttpEntityStrict: an entity containing already evaluated ByteString data
2221
* - HttpEntityDefault: the default entity which has a known length and which contains
2322
* a stream of ByteStrings.
2423
* - HttpEntityChunked: represents an entity that is delivered using `Transfer-Encoding: chunked`
25-
* - HttpEntityCloseDelimited: the entity which doesn't have a fixed length but which is delimited by
24+
* - HttpEntityCloseDelimited: an entity which doesn't have a fixed length but which is delimited by
2625
* closing the connection.
26+
* - HttpEntityIndefiniteLength: an entity which doesn't have a fixed length which can be used to construct BodyParts
27+
* with indefinite length
28+
*
29+
* Marker-interfaces denote which subclasses can be used in which context:
30+
* - RequestEntity: an entity type that can be used in an HttpRequest
31+
* - ResponseEntity: an entity type that can be used in an HttpResponse
32+
* - BodyPartEntity: an entity type that can be used in a BodyPart
33+
* - UniversalEntity: an entity type that can be used in every context
2734
*
28-
* All entity subtypes but HttpEntityCloseDelimited are subtypes of {@link HttpEntityRegular} which
29-
* means they can be used in Http request that disallow close-delimited transfer of the entity.
35+
* Use the static constructors in HttpEntities to construct instances.
3036
*/
31-
public abstract class HttpEntity {
37+
public interface HttpEntity {
3238
/**
3339
* Returns the content-type of this entity
3440
*/
@@ -45,11 +51,6 @@ public abstract class HttpEntity {
4551
*/
4652
public abstract boolean isKnownEmpty();
4753

48-
/**
49-
* Returns if this entity is a subtype of HttpEntityRegular.
50-
*/
51-
public abstract boolean isRegular();
52-
5354
/**
5455
* Returns if this entity is a subtype of HttpEntityChunked.
5556
*/
@@ -65,46 +66,13 @@ public abstract class HttpEntity {
6566
*/
6667
public abstract boolean isCloseDelimited();
6768

69+
/**
70+
* Returns if this entity is a subtype of HttpEntityIndefiniteLength.
71+
*/
72+
public abstract boolean isIndefiniteLength();
73+
6874
/**
6975
* Returns a stream of data bytes this entity consists of.
7076
*/
7177
public abstract Publisher<ByteString> getDataBytes(FlowMaterializer materializer);
72-
73-
public static HttpEntityStrict create(String string) {
74-
return HttpEntity$.MODULE$.apply(string);
75-
}
76-
public static HttpEntityStrict create(byte[] bytes) {
77-
return HttpEntity$.MODULE$.apply(bytes);
78-
}
79-
public static HttpEntityStrict create(ByteString bytes) {
80-
return HttpEntity$.MODULE$.apply(bytes);
81-
}
82-
public static HttpEntityStrict create(ContentType contentType, String string) {
83-
return HttpEntity$.MODULE$.apply((akka.http.model.ContentType) contentType, string);
84-
}
85-
public static HttpEntityStrict create(ContentType contentType, byte[] bytes) {
86-
return HttpEntity$.MODULE$.apply((akka.http.model.ContentType) contentType, bytes);
87-
}
88-
public static HttpEntityStrict create(ContentType contentType, ByteString bytes) {
89-
return HttpEntity$.MODULE$.apply((akka.http.model.ContentType) contentType, bytes);
90-
}
91-
public static HttpEntityRegular create(ContentType contentType, File file) {
92-
return (HttpEntityRegular) HttpEntity$.MODULE$.apply((akka.http.model.ContentType) contentType, file);
93-
}
94-
public static HttpEntityDefault create(ContentType contentType, long contentLength, Publisher<ByteString> data) {
95-
return new akka.http.model.HttpEntity.Default((akka.http.model.ContentType) contentType, contentLength, data);
96-
}
97-
public static HttpEntityCloseDelimited createCloseDelimited(ContentType contentType, Publisher<ByteString> data) {
98-
return new akka.http.model.HttpEntity.CloseDelimited((akka.http.model.ContentType) contentType, data);
99-
}
100-
public static HttpEntityChunked createChunked(ContentType contentType, Publisher<ChunkStreamPart> chunks) {
101-
return new akka.http.model.HttpEntity.Chunked(
102-
(akka.http.model.ContentType) contentType,
103-
Util.<ChunkStreamPart, akka.http.model.HttpEntity.ChunkStreamPart>upcastPublisher(chunks));
104-
}
105-
public static HttpEntityChunked createChunked(ContentType contentType, Publisher<ByteString> data, FlowMaterializer materializer) {
106-
return akka.http.model.HttpEntity.Chunked$.MODULE$.fromData(
107-
(akka.http.model.ContentType) contentType,
108-
data, materializer);
109-
}
11078
}

akka-http-core/src/main/java/akka/http/model/japi/HttpEntityChunked.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@
1010
* Represents an entity transferred using `Transfer-Encoding: chunked`. It consists of a
1111
* stream of {@link ChunkStreamPart}.
1212
*/
13-
public abstract class HttpEntityChunked extends HttpEntityRegular {
13+
public abstract class HttpEntityChunked implements RequestEntity, ResponseEntity {
1414
public abstract Publisher<ChunkStreamPart> getChunks();
1515
}

akka-http-core/src/main/java/akka/http/model/japi/HttpEntityCloseDelimited.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212
* determined by closing the underlying connection. Therefore, this entity type is only
1313
* available for Http responses.
1414
*/
15-
public abstract class HttpEntityCloseDelimited extends HttpEntity {
15+
public abstract class HttpEntityCloseDelimited implements ResponseEntity {
1616
public abstract Publisher<ByteString> data();
1717
}

akka-http-core/src/main/java/akka/http/model/japi/HttpEntityDefault.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
/**
1111
* The default entity type which has a predetermined length and a stream of data bytes.
1212
*/
13-
public abstract class HttpEntityDefault extends HttpEntityRegular {
13+
public abstract class HttpEntityDefault implements BodyPartEntity, RequestEntity, ResponseEntity {
1414
public abstract long contentLength();
1515
public abstract Publisher<ByteString> data();
1616
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
3+
*/
4+
5+
package akka.http.model.japi;
6+
7+
import akka.util.ByteString;
8+
import org.reactivestreams.Publisher;
9+
10+
/**
11+
* Represents an entity without a predetermined content-length to use in a BodyParts.
12+
*/
13+
public abstract class HttpEntityIndefiniteLength implements BodyPartEntity {
14+
public abstract Publisher<ByteString> data();
15+
}

akka-http-core/src/main/java/akka/http/model/japi/HttpEntityRegular.java

Lines changed: 0 additions & 10 deletions
This file was deleted.

akka-http-core/src/main/java/akka/http/model/japi/HttpEntityStrict.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99
/**
1010
* The entity type which consists of a predefined fixed ByteString of data.
1111
*/
12-
public abstract class HttpEntityStrict extends HttpEntityRegular {
12+
public abstract class HttpEntityStrict implements BodyPartEntity, RequestEntity, ResponseEntity {
1313
public abstract ByteString data();
1414
}

akka-http-core/src/main/java/akka/http/model/japi/HttpMessage.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public interface HttpMessage {
4848
/**
4949
* The entity of this message.
5050
*/
51-
HttpEntity entity();
51+
ResponseEntity entity();
5252

5353
public static interface MessageTransformations<Self> {
5454
/**
@@ -71,11 +71,6 @@ public static interface MessageTransformations<Self> {
7171
*/
7272
Self removeHeader(String headerName);
7373

74-
/**
75-
* Returns a copy of this message with a new entity.
76-
*/
77-
Self withEntity(HttpEntity entity);
78-
7974
/**
8075
* Returns a copy of this message with a new entity.
8176
*/
@@ -110,5 +105,10 @@ public static interface MessageTransformations<Self> {
110105
* Returns a copy of Self message with a new entity.
111106
*/
112107
Self withEntity(ContentType type, File file);
108+
109+
/**
110+
* Returns a copy of Self message with a new entity.
111+
*/
112+
Self withEntity(RequestEntity entity);
113113
}
114114
}

akka-http-core/src/main/java/akka/http/model/japi/HttpRequest.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public abstract class HttpRequest implements HttpMessage, HttpMessage.MessageTra
2121
/**
2222
* Returns the entity of this request.
2323
*/
24-
public abstract HttpEntityRegular entity();
24+
public abstract RequestEntity entity();
2525

2626
/**
2727
* Returns a copy of this instance with a new method.
@@ -38,6 +38,11 @@ public abstract class HttpRequest implements HttpMessage, HttpMessage.MessageTra
3838
*/
3939
public abstract HttpRequest withUri(String path);
4040

41+
/**
42+
* Returns a copy of this instance with a new entity.
43+
*/
44+
public abstract HttpRequest withEntity(RequestEntity entity);
45+
4146
/**
4247
* Returns a default request to be changed using the `withX` methods.
4348
*/

akka-http-core/src/main/java/akka/http/model/japi/HttpResponse.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ public abstract class HttpResponse implements HttpMessage, HttpMessage.MessageTr
1313
*/
1414
public abstract StatusCode status();
1515

16+
/**
17+
* Returns the entity of this request.
18+
*/
19+
public abstract ResponseEntity entity();
20+
1621
/**
1722
* Returns a copy of this instance with a new status-code.
1823
*/
@@ -23,6 +28,11 @@ public abstract class HttpResponse implements HttpMessage, HttpMessage.MessageTr
2328
*/
2429
public abstract HttpResponse withStatus(int statusCode);
2530

31+
/**
32+
* Returns a copy of this instance with a new entity.
33+
*/
34+
public abstract HttpResponse withEntity(ResponseEntity entity);
35+
2636
/**
2737
* Returns a default response to be changed using the `withX` methods.
2838
*/
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
3+
*/
4+
5+
package akka.http.model.japi;
6+
7+
/** Marker-interface for entity types that can be used in a request */
8+
public interface RequestEntity extends ResponseEntity {}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
3+
*/
4+
5+
package akka.http.model.japi;
6+
7+
/** Marker-interface for entity types that can be used in a response */
8+
public interface ResponseEntity extends HttpEntity {}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
3+
*/
4+
5+
package akka.http.model.japi;
6+
7+
/** Marker-interface for entity types that can be used in any context */
8+
public interface UniversalEntity extends RequestEntity, ResponseEntity, BodyPartEntity {}

akka-http-core/src/main/scala/akka/http/engine/parsing/BodyPartParser.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ private[http] final class BodyPartParser(defaultContentType: ContentType,
143143
def parseEntity(headers: List[HttpHeader], contentType: ContentType,
144144
emitPartChunk: (List[HttpHeader], ContentType, ByteString) Unit = {
145145
(headers, ct, bytes)
146-
emit(BodyPartStart(headers, entityParts HttpEntity.CloseDelimited(ct,
146+
emit(BodyPartStart(headers, entityParts HttpEntity.IndefiniteLength(ct,
147147
Flow(entityParts).collect { case EntityPart(data) data }.toPublisher())))
148148
emit(bytes)
149149
},
@@ -217,7 +217,7 @@ private[http] object BodyPartParser {
217217
val boundaryCharNoSpace = CharPredicate.Digit ++ CharPredicate.Alpha ++ "'()+_,-./:=?"
218218

219219
sealed trait Output
220-
final case class BodyPartStart(headers: List[HttpHeader], createEntity: Publisher[Output] HttpEntity) extends Output
220+
final case class BodyPartStart(headers: List[HttpHeader], createEntity: Publisher[Output] BodyPartEntity) extends Output
221221
final case class EntityPart(data: ByteString) extends Output
222222
final case class ParseError(info: ErrorInfo) extends Output
223223

akka-http-core/src/main/scala/akka/http/engine/parsing/HttpMessageParser.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,19 +231,19 @@ private[http] abstract class HttpMessageParser[Output >: ParserOutput.MessageOut
231231
case None ContentTypes.`application/octet-stream`
232232
}
233233

234-
def emptyEntity(cth: Option[`Content-Type`])(entityParts: Any): HttpEntity.Regular =
234+
def emptyEntity(cth: Option[`Content-Type`])(entityParts: Any): UniversalEntity =
235235
if (cth.isDefined) HttpEntity.empty(cth.get.contentType) else HttpEntity.Empty
236236

237237
def strictEntity(cth: Option[`Content-Type`], input: ByteString, bodyStart: Int,
238-
contentLength: Int)(entityParts: Any): HttpEntity.Regular =
238+
contentLength: Int)(entityParts: Any): UniversalEntity =
239239
HttpEntity.Strict(contentType(cth), input.slice(bodyStart, bodyStart + contentLength))
240240

241-
def defaultEntity(cth: Option[`Content-Type`], contentLength: Long)(entityParts: Publisher[_ <: ParserOutput])(implicit fm: FlowMaterializer): HttpEntity.Regular = {
241+
def defaultEntity(cth: Option[`Content-Type`], contentLength: Long)(entityParts: Publisher[_ <: ParserOutput])(implicit fm: FlowMaterializer): UniversalEntity = {
242242
val data = Flow(entityParts).collect { case ParserOutput.EntityPart(bytes) bytes }.toPublisher()
243243
HttpEntity.Default(contentType(cth), contentLength, data)
244244
}
245245

246-
def chunkedEntity(cth: Option[`Content-Type`])(entityChunks: Publisher[_ <: ParserOutput])(implicit fm: FlowMaterializer): HttpEntity.Regular = {
246+
def chunkedEntity(cth: Option[`Content-Type`])(entityChunks: Publisher[_ <: ParserOutput])(implicit fm: FlowMaterializer): RequestEntity with ResponseEntity = {
247247
val chunks = Flow(entityChunks).collect { case ParserOutput.EntityChunk(chunk) chunk }.toPublisher()
248248
HttpEntity.Chunked(contentType(cth), chunks)
249249
}

akka-http-core/src/main/scala/akka/http/engine/parsing/HttpRequestParser.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ private[http] class HttpRequestParser(_settings: ParserSettings,
108108
clh: Option[`Content-Length`], cth: Option[`Content-Type`], teh: Option[`Transfer-Encoding`],
109109
hostHeaderPresent: Boolean, closeAfterResponseCompletion: Boolean): StateResult =
110110
if (hostHeaderPresent || protocol == HttpProtocols.`HTTP/1.0`) {
111-
def emitRequestStart(createEntity: Publisher[ParserOutput.RequestOutput] HttpEntity.Regular) =
111+
def emitRequestStart(createEntity: Publisher[ParserOutput.RequestOutput] RequestEntity) =
112112
emit(ParserOutput.RequestStart(method, uri, protocol, headers, createEntity, closeAfterResponseCompletion))
113113

114114
teh match {

0 commit comments

Comments
 (0)