final void writeHeaders(RequestHeaders headers, boolean needs100Continue) {
        assert session != null;
        final SessionProtocol protocol = session.protocol();
        assert protocol != null;
        if (needs100Continue) {
            state = State.NEEDS_100_CONTINUE;
        } else if (headersOnly) {
            state = State.DONE;
        } else if (allowTrailers) {
            state = State.NEEDS_DATA_OR_TRAILERS;
        } else {
            state = State.NEEDS_DATA;
        }

        logBuilder.requestHeaders(headers);

        final String connectionOption = headers.get(HttpHeaderNames.CONNECTION);
        if (CLOSE_STRING.equalsIgnoreCase(connectionOption) || !keepAlive) {
            // Make the session unhealthy so that subsequent requests do not use it.
            // In HTTP/2 request, the "Connection: close" is just interpreted as a signal to close the
            // connection by sending a GOAWAY frame that will be sent after receiving the corresponding
            // response from the remote peer. The "Connection: close" header is stripped when it is converted to
            // a Netty HTTP/2 header.
            session.markUnacquirable();
        }

        final ChannelPromise promise = ch.newPromise();
        // Attach a listener first to make the listener early handle a cause raised while writing headers
        // before any other callbacks like `onStreamClosed()` are invoked.
        promise.addListener(this);
        encoder.writeHeaders(id, streamId(), headers, headersOnly, promise);
    }
Writes the {@link RequestHeaders} to the {@link Channel}.
The {@link RequestHeaders} is merged with {@link ClientRequestContext#additionalRequestHeaders()}
before being written.
Note that the written data is not flushed by this method. The caller should explicitly call
{@link Channel#flush()} when each write unit is done.
core/src/main/java/com/linecorp/armeria/client/AbstractHttpRequestHandler.java