> Docs > Http Server > Entity and Body

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.

Request Entity

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 */ } )

Response Entity

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.

ByteSource

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;
    }
}

HttpEntity

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());

Entity Metadata

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.

Async Body

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;
    }

Response Body as Sink

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.