public final void emitHeader()

in java/org/apache/coyote/http2/Stream.java [319:484]


    public final void emitHeader(String name, String value) throws HpackException {
        if (log.isTraceEnabled()) {
            log.trace(sm.getString("stream.header.debug", getConnectionId(), getIdAsString(), name, value));
        }

        // Header names must be lowercase
        if (!name.toLowerCase(Locale.US).equals(name)) {
            throw new HpackException(sm.getString("stream.header.case", getConnectionId(), getIdAsString(), name));
        }

        if (HTTP_CONNECTION_SPECIFIC_HEADERS.contains(name)) {
            throw new HpackException(
                    sm.getString("stream.header.connection", getConnectionId(), getIdAsString(), name));
        }

        if ("te".equals(name)) {
            if (!"trailers".equals(value)) {
                throw new HpackException(sm.getString("stream.header.te", getConnectionId(), getIdAsString(), value));
            }
        }

        if (headerException != null) {
            // Don't bother processing the header since the stream is going to
            // be reset anyway
            return;
        }

        if (name.isEmpty()) {
            throw new HpackException(sm.getString("stream.header.empty", getConnectionId(), getIdAsString()));
        }

        boolean pseudoHeader = name.charAt(0) == ':';

        if (pseudoHeader && headerState != HEADER_STATE_PSEUDO) {
            headerException = new StreamException(
                    sm.getString("stream.header.unexpectedPseudoHeader", getConnectionId(), getIdAsString(), name),
                    Http2Error.PROTOCOL_ERROR, getIdAsInt());
            // No need for further processing. The stream will be reset.
            return;
        }

        if (headerState == HEADER_STATE_PSEUDO && !pseudoHeader) {
            headerState = HEADER_STATE_REGULAR;
        }

        switch (name) {
            case ":method": {
                if (coyoteRequest.method().isNull()) {
                    coyoteRequest.method().setString(value);
                    if ("HEAD".equals(value)) {
                        configureVoidOutputFilter();
                    }
                } else {
                    throw new HpackException(
                            sm.getString("stream.header.duplicate", getConnectionId(), getIdAsString(), ":method"));
                }
                break;
            }
            case ":scheme": {
                if (coyoteRequest.scheme().isNull()) {
                    coyoteRequest.scheme().setString(value);
                } else {
                    throw new HpackException(
                            sm.getString("stream.header.duplicate", getConnectionId(), getIdAsString(), ":scheme"));
                }
                break;
            }
            case ":path": {
                if (!coyoteRequest.requestURI().isNull()) {
                    throw new HpackException(
                            sm.getString("stream.header.duplicate", getConnectionId(), getIdAsString(), ":path"));
                }
                if (value.isEmpty()) {
                    throw new HpackException(sm.getString("stream.header.noPath", getConnectionId(), getIdAsString()));
                }
                int queryStart = value.indexOf('?');
                String uri;
                if (queryStart == -1) {
                    uri = value;
                } else {
                    uri = value.substring(0, queryStart);
                    String query = value.substring(queryStart + 1);
                    coyoteRequest.queryString().setString(query);
                }
                // Bug 61120. Set the URI as bytes rather than String so:
                // - any path parameters are correctly processed
                // - the normalization security checks are performed that prevent
                // directory traversal attacks
                byte[] uriBytes = uri.getBytes(StandardCharsets.ISO_8859_1);
                coyoteRequest.requestURI().setBytes(uriBytes, 0, uriBytes.length);
                break;
            }
            case ":authority": {
                if (coyoteRequest.serverName().isNull()) {
                    parseAuthority(value, false);
                } else {
                    throw new HpackException(
                            sm.getString("stream.header.duplicate", getConnectionId(), getIdAsString(), ":authority"));
                }
                break;
            }
            case "cookie": {
                // Cookie headers need to be concatenated into a single header
                // See RFC 7540 8.1.2.5
                if (cookieHeader == null) {
                    cookieHeader = new StringBuilder();
                } else {
                    cookieHeader.append("; ");
                }
                cookieHeader.append(value);
                break;
            }
            case "host": {
                if (coyoteRequest.serverName().isNull()) {
                    // No :authority header. This is first host header. Use it.
                    hostHeaderSeen = true;
                    parseAuthority(value, true);
                } else if (!hostHeaderSeen) {
                    // First host header - must be consistent with :authority
                    hostHeaderSeen = true;
                    compareAuthority(value);
                } else {
                    // Multiple hosts headers - illegal
                    throw new HpackException(
                            sm.getString("stream.header.duplicate", getConnectionId(), getIdAsString(), "host"));
                }
                break;
            }
            case "priority": {
                try {
                    Priority p = Priority.parsePriority(new StringReader(value));
                    setUrgency(p.getUrgency());
                    setIncremental(p.getIncremental());
                } catch (IOException ioe) {
                    // Not possible with StringReader
                } catch (IllegalArgumentException iae) {
                    // Invalid priority header field values should be ignored
                    if (log.isTraceEnabled()) {
                        log.trace(sm.getString("http2Parser.processFramePriorityUpdate.invalid", getConnectionId(),
                                getIdAsString()), iae);
                    }
                }
                break;
            }
            default: {
                if (headerState == HEADER_STATE_TRAILER && !handler.getProtocol().isTrailerHeaderAllowed(name)) {
                    break;
                }
                if ("expect".equals(name) && "100-continue".equals(value)) {
                    coyoteRequest.setExpectation(true);
                }
                if (pseudoHeader) {
                    headerException = new StreamException(
                            sm.getString("stream.header.unknownPseudoHeader", getConnectionId(), getIdAsString(), name),
                            Http2Error.PROTOCOL_ERROR, getIdAsInt());
                }

                if (headerState == HEADER_STATE_TRAILER) {
                    // HTTP/2 headers are already always lower case
                    coyoteRequest.getMimeTrailerFields().addValue(name).setString(value);
                } else {
                    coyoteRequest.getMimeHeaders().addValue(name).setString(value);
                }
            }
        }
    }