final ChannelFuture writeAggregatedHttpResponse(AggregatedHttpResponse res) {
        final int id = req.id();
        final int streamId = req.streamId();

        final ServerConfig config = reqCtx.config().server().config();
        ResponseHeaders headers = mergeResponseHeaders(res.headers(), reqCtx.additionalResponseHeaders(),
                                                       reqCtx.config().defaultHeaders(),
                                                       config.isServerHeaderEnabled(),
                                                       config.isDateHeaderEnabled());
        final String connectionOption = headers.get(HttpHeaderNames.CONNECTION);
        if (CLOSE_STRING.equalsIgnoreCase(connectionOption)) {
            disconnectWhenFinished();
        }

        final HttpData content = res.content();
        content.touch(reqCtx);
        // An aggregated response always has empty content if its status.isContentAlwaysEmpty() is true.
        assert !res.status().isContentAlwaysEmpty() || content.isEmpty();
        final boolean contentEmpty;
        if (content.isEmpty()) {
            contentEmpty = true;
        } else if (req.method() == HttpMethod.HEAD) {
            contentEmpty = true;
            // Need to release the body because we're not passing it over to the encoder.
            content.close();
        } else {
            contentEmpty = false;
        }

        final HttpHeaders trailers = mergeTrailers(res.trailers(), reqCtx.additionalResponseTrailers());
        final boolean trailersEmpty = trailers.isEmpty();

        if (reqCtx.sessionProtocol().isMultiplex() && !contentEmpty && headers.contentLength() == -1) {
            // If a trailers is set, a content-length is automatically removed by
            // `ArmeriaHttpUtil.setOrRemoveContentLength()` when creating `AggregatedHttpResponse`.
            // However, in HTTP/2, a content-length could be set with a trailers.
            headers = headers.toBuilder()
                             .contentLength(content.length())
                             .build();
        }

        final HttpMethod method = reqCtx.method();
        if (!res.informationals().isEmpty()) {
            for (ResponseHeaders informational : res.informationals()) {
                responseEncoder.writeHeaders(id, streamId, informational,
                                             false, trailersEmpty, method);
            }
        }
        logBuilder().responseHeaders(headers);
        ChannelFuture future =
                responseEncoder.writeHeaders(id, streamId, headers, contentEmpty && trailersEmpty,
                                             trailersEmpty, method);
        if (!contentEmpty) {
            logBuilder().increaseResponseLength(content);
            future = responseEncoder.writeData(id, streamId, content, trailersEmpty);
        }
        if (!trailersEmpty) {
            logBuilder().responseTrailers(trailers);
            future = responseEncoder.writeTrailers(id, streamId, trailers);
        }
        return future;
    }
Writes the {@link AggregatedHttpResponse} to the {@link Channel}.
Note that the caller has to flush the written data when needed.
core/src/main/java/com/linecorp/armeria/server/AbstractHttpResponseHandler.java