protected final void prepareResponse()

in java/org/apache/coyote/http11/Http11Processor.java [864:1044]


    protected final void prepareResponse() throws IOException {

        boolean entityBody = true;
        contentDelimitation = false;

        OutputFilter[] outputFilters = outputBuffer.getFilters();

        if (http09) {
            // HTTP/0.9
            outputBuffer.addActiveFilter(outputFilters[Constants.IDENTITY_FILTER]);
            outputBuffer.commit();
            return;
        }

        int statusCode = response.getStatus();
        if (statusCode < 200 || statusCode == 204 || statusCode == 205 || statusCode == 304) {
            // No entity body
            outputBuffer.addActiveFilter(outputFilters[Constants.VOID_FILTER]);
            entityBody = false;
            contentDelimitation = true;
            if (statusCode == 205) {
                // RFC 7231 requires the server to explicitly signal an empty
                // response in this case
                response.setContentLength(0);
            } else {
                response.setContentLength(-1);
            }
        }

        boolean head = request.method().equals("HEAD");
        if (head) {
            // Any entity body, if present, should not be sent
            outputBuffer.addActiveFilter(outputFilters[Constants.VOID_FILTER]);
            contentDelimitation = true;
        }

        // Sendfile support
        if (protocol.getUseSendfile()) {
            prepareSendfile(outputFilters);
        }

        // Check for compression
        boolean useCompression = false;
        if (entityBody && sendfileData == null) {
            useCompression = protocol.useCompression(request, response);
        }

        MimeHeaders headers = response.getMimeHeaders();
        // A SC_NO_CONTENT response may include entity headers
        if (entityBody || statusCode == HttpServletResponse.SC_NO_CONTENT) {
            String contentType = response.getContentType();
            if (contentType != null) {
                headers.setValue("Content-Type").setString(contentType);
            }
            String contentLanguage = response.getContentLanguage();
            if (contentLanguage != null) {
                headers.setValue("Content-Language").setString(contentLanguage);
            }
        }

        long contentLength = response.getContentLengthLong();
        boolean connectionClosePresent = isConnectionToken(headers, Constants.CLOSE);
        if (http11 && response.getTrailerFields() != null) {
            // If trailer fields are set, always use chunking
            outputBuffer.addActiveFilter(outputFilters[Constants.CHUNKED_FILTER]);
            contentDelimitation = true;
            headers.addValue(Constants.TRANSFERENCODING).setString(Constants.CHUNKED);
        } else if (contentLength != -1) {
            headers.setValue("Content-Length").setLong(contentLength);
            outputBuffer.addActiveFilter(outputFilters[Constants.IDENTITY_FILTER]);
            contentDelimitation = true;
        } else if (head) {
            /*
             * The OutputBuffer can't differentiate between a zero length response because GET would have produced a
             * zero length body and a zero length response because HEAD didn't write any content. Therefore skip setting
             * the "transfer-encoding: chunked" header as that may not be consistent with what would be seen with the
             * equivalent GET.
             */
        } else {
            // If the response code supports an entity body and we're on
            // HTTP 1.1 then we chunk unless we have a Connection: close header
            if (http11 && entityBody && !connectionClosePresent) {
                outputBuffer.addActiveFilter(outputFilters[Constants.CHUNKED_FILTER]);
                contentDelimitation = true;
                headers.addValue(Constants.TRANSFERENCODING).setString(Constants.CHUNKED);
            } else {
                outputBuffer.addActiveFilter(outputFilters[Constants.IDENTITY_FILTER]);
            }
        }

        if (useCompression) {
            outputBuffer.addActiveFilter(outputFilters[Constants.GZIP_FILTER]);
        }

        // Add date header unless application has already set one (e.g. in a
        // Caching Filter)
        if (headers.getValue("Date") == null) {
            headers.addValue("Date").setString(FastHttpDateFormat.getCurrentDate());
        }

        // Although using transfer-encoding for gzip would be doable and was
        // the original intent (which means the compression would be from an
        // endpoint to the next, so only for the current transmission), it
        // has been found that using content-encoding (which is end to end
        // compression) is more efficient and more reliable.

        if ((entityBody) && (!contentDelimitation) || connectionClosePresent) {
            // Disable keep-alive if:
            // - there is a response body but way for the client to determine
            // the content length information; or
            // - there is a "connection: close" header present
            // This will cause the "connection: close" header to be added if it
            // is not already present.
            keepAlive = false;
        }

        // This may disable keep-alive so check before working out the Connection header
        checkExpectationAndResponseStatus();

        // This may disable keep-alive if there is more body to swallow
        // than the configuration allows
        checkMaxSwallowSize();

        // If we know that the request is bad this early, add the
        // Connection: close header.
        if (keepAlive && statusDropsConnection(statusCode)) {
            keepAlive = false;
        }
        if (!keepAlive) {
            // Avoid adding the close header twice
            if (!connectionClosePresent) {
                headers.addValue(Constants.CONNECTION).setString(Constants.CLOSE);
            }
        } else if (!getErrorState().isError()) {
            if (!http11) {
                headers.addValue(Constants.CONNECTION).setString(Constants.KEEP_ALIVE_HEADER_VALUE_TOKEN);
            }

            if (protocol.getUseKeepAliveResponseHeader()) {
                boolean connectionKeepAlivePresent =
                        isConnectionToken(request.getMimeHeaders(), Constants.KEEP_ALIVE_HEADER_VALUE_TOKEN);

                if (connectionKeepAlivePresent) {
                    int keepAliveTimeout = protocol.getKeepAliveTimeout();

                    if (keepAliveTimeout > 0) {
                        String value = "timeout=" + keepAliveTimeout / 1000L;
                        headers.setValue(Constants.KEEP_ALIVE_HEADER_NAME).setString(value);

                        if (http11) {
                            // Append if there is already a Connection header,
                            // else create the header
                            MessageBytes connectionHeaderValue = headers.getValue(Constants.CONNECTION);
                            if (connectionHeaderValue == null) {
                                headers.addValue(Constants.CONNECTION)
                                        .setString(Constants.KEEP_ALIVE_HEADER_VALUE_TOKEN);
                            } else {
                                connectionHeaderValue.setString(connectionHeaderValue.getString() + ", " +
                                        Constants.KEEP_ALIVE_HEADER_VALUE_TOKEN);
                            }
                        }
                    }
                }
            }
        }

        // Add server header
        String server = protocol.getServer();
        if (server == null) {
            if (protocol.getServerRemoveAppProvidedValues()) {
                headers.removeHeader("server");
            }
        } else {
            // server always overrides anything the app might set
            headers.setValue("Server").setString(server);
        }

        writeHeaders(response.getStatus(), headers);

        outputBuffer.commit();
    }