@Nullable
    private HttpResponse checkOrigin(ServiceRequestContext ctx, RequestHeaders headers) {
        if (allowAnyOrigin) {
            return null;
        }
        final String origin = headers.get(HttpHeaderNames.ORIGIN, "");
        if (origin.isEmpty()) {
            return HttpResponse.of(HttpStatus.FORBIDDEN, MediaType.PLAIN_TEXT_UTF_8,
                                   "missing the origin header");
        }

        final String lowerCaseOrigin = Ascii.toLowerCase(origin);
        if (originPredicate == null) {
            // Only the same-origin is allowed.
            if (!isSameOrigin(ctx, headers, lowerCaseOrigin)) {
                return HttpResponse.of(HttpStatus.FORBIDDEN, MediaType.PLAIN_TEXT_UTF_8,
                                       "not allowed origin: " + lowerCaseOrigin);
            }
            return null;
        }
        if (!originPredicate.test(lowerCaseOrigin)) {
            return HttpResponse.of(HttpStatus.FORBIDDEN, MediaType.PLAIN_TEXT_UTF_8,
                                   "not allowed origin: " + lowerCaseOrigin);
        }
        return null;
    }
Handles the HTTP/2 web socket handshake described in
<a href="https://datatracker.ietf.org/doc/html/rfc8441">Bootstrapping WebSockets with HTTP/2</a>.
These are examples of a request and the corresponding response:

<p>Request:
<pre>
HEADERS + END_HEADERS
:method = CONNECT
:protocol = websocket
:scheme = https
:path = /chat
:authority = server.example.com
sec-websocket-protocol = chat, superchat
sec-websocket-extensions = permessage-deflate
sec-websocket-version = 13
origin = http://www.example.com
</pre>

<p>Response:
<pre>
HEADERS + END_HEADERS
:status = 200
sec-websocket-protocol = chat
</pre>
core/src/main/java/com/linecorp/armeria/internal/server/websocket/DefaultWebSocketService.java