> Docs > Http Server > Entity and Body
An http request or response may contain an "entity", representing the resource being transferred. This is modeled by interface HttpEntity
, which contains metadata like contentType
, and a body
which is an async byte source.
Whether an http request contains an entity usually depends on the request method, see HttpRequest.entity()
.
See Form Handling and Json for parsing Form and Json request bodies.
If you want to directly handle entity body bytes, read them from entity.body()
which is an async ByteSource
. If the body is expected to be small, you can use the convenience methods readAll
or asString
Async<ByteBuffer> bodyBytes = body.readAll( /*max*/1000 ); // or Async<String> bodyString = body.asString( /*max*/1000 );
If the body is potentially big, try
AsyncIterator.forEach( body::read, (ByteBuffer bb)->{ /* handle bytes */ } )
In most cases, an HttpResponse
contains an entity, see HttpResponse.entity()
.
Note that the response entity body is a ByteSource
, not a sink. The server pulls data from the body when it needs to.
Bayou provides some implementations of HttpEntity
and ByteSource
, for example, FileHttpEntity
, FileByteSource
.
In the following sections, we will program a custom entity and body from scratch.
The ByteSource.read()
method yields data asynchronously. If EOF is reached, the read action fails with End
.
Let's define a source that yields letters 'A'
to 'Z'
, one letter at a time
public class AzSource implements ByteSource { byte c = 'A'; @Override public Async<ByteBuffer> read() { if(c>'Z') return End.async(); ByteBuffer bb = ByteBuffer.wrap(new byte[]{c++}); return Async.success(bb); } @Override public Async<Void> close() { return Async.VOID; } }
The minimal requirement for an entity is body
and contentType
public class AzEntity implements HttpEntity { @Override public ByteSource body() { return new AzSource(); } @Override public ContentType contentType() { return ContentType.text_plain_US_ASCII; } }
Now we can create a response with the entity
return new HttpResponseImpl(HttpStatus.c200_OK, new AzEntity());
The entity's contentLength
is optional. If not provided, the response body will be "chunked". Since AzSource.read()
produces data immediately, actually only one chunk is created containing all 26 bytes.
If contentLength
is provided, it must be consistent with the number of bytes in the body
@Override public Long contentLength() { return 26L; }
Since the entity is a never-changing constant, we may give it a very long expiration, and fixed Last-Modified
and ETag
@Override public Instant expires() { return Instant.MAX; } @Override public Instant lastModified() { return Instant.MIN; }
The default etag()
is based on lastModified
, which is fine, but probably unnecessarily long here. We can just assign it a constant value
@Override public String etag() { return "0"; }
For a more realistic entity, we should assign more sensible Expires
, Last-Modified
, ETag
values.
Let's add some delays to AzSource.read()
, so that it is genuinely asynchronous
@Override public Async<ByteBuffer> read() { return Fiber .sleep(Duration.ofSeconds(1)) .map( v-> read0() ); } ByteBuffer read0() throws End { if(c>'Z') throw End.instance(); return ByteBuffer.wrap(new byte[]{c++}); }
On the browser, we'll see that letters are coming out one at a time.
We can optionally implement ByteSource.skip(n)
to help Range requests:
@Override public long skip(long n) { c += n; // bug: numeric overflow return n; }
If you want to treat the response body as a ByteSink
instead, you can use BytePipe
public class MyEntity implements HttpEntity { @Override public ByteSource body() { BytePipe pipe = new BytePipe(); ByteSink sink = pipe.sink(); AsyncIterator .ofInts('A', 'A'+26) .forEach_( i-> sink.write( i2bb(i) ) ) .finally_( sink::close ); return pipe.source(); } ByteBuffer i2bb(int i) { return ByteBuffer.wrap(new byte[]{(byte)i}); }
However, async programming with sink is not easy in general. You are probably better off with modeling the body as a ByteSource
, even if it requires a lot of bookkeeping of internal states.