Repository: apache/dubbo
File: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/AbstractServerHttpChannelObserver.java (Java)

Code before:

    protected final int resolveStatusCode(Object data) {
        if (data instanceof HttpResult) {
            int status = ((HttpResult<?>) data).getStatus();
            if (status >= 100) {
                return status;
            }
        }
        return HttpStatus.OK.getCode();
    }

    protected final HttpMetadata buildMetadata(
            int statusCode, Object data, Throwable throwable, HttpOutputMessage message) {
        HttpMetadata metadata = encodeHttpMetadata(message == null);
        HttpHeaders headers = metadata.headers();
        headers.set(HttpHeaderNames.STATUS.getKey(), HttpUtils.toStatusString(statusCode));
        if (message != null) {
            headers.set(HttpHeaderNames.CONTENT_TYPE.getKey(), responseEncoder.contentType());
        }
        if (data instanceof HttpResult) {
            HttpResult<?> result = (HttpResult<?>) data;
            if (result.getHeaders() != null) {
                headers.set(result.getHeaders());
            }
        }
        customizeHeaders(headers, throwable, message);
        return metadata;
    }

    protected abstract HttpMetadata encodeHttpMetadata(boolean endStream);

    protected void customizeHeaders(HttpHeaders headers, Throwable throwable, HttpOutputMessage message) {
        List<BiConsumer<HttpHeaders, Throwable>> headersCustomizers = this.headersCustomizers;
        if (headersCustomizers != null) {
            for (int i = 0, size = headersCustomizers.size(); i < size; i++) {
                headersCustomizers.get(i).accept(headers, throwable);
            }
        }
    }

    protected final void sendMetadata(HttpMetadata metadata) {
        if (headerSent) {
            return;
        }
        getHttpChannel().writeHeader(metadata);
        headerSent = true;
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Http response headers sent: " + metadata.headers());
        }
    }


Review comment (suggestion):
Moving customizeHeaders before applying HttpResult headers causes custom header values to be overwritten. To preserve custom headers, invoke customizeHeaders after merging HttpResult headers.
```suggestion
        if (data instanceof HttpResult) {
            HttpResult<?> result = (HttpResult<?>) data;
            if (result.getHeaders() != null) {
                headers.set(result.getHeaders());
            }
        }
        customizeHeaders(headers, throwable, message);
```

Code after:

    protected final int resolveStatusCode(Object data) {
        if (data instanceof HttpResult) {
            int status = ((HttpResult<?>) data).getStatus();
            if (status >= 100) {
                return status;
            }
        }
        return HttpStatus.OK.getCode();
    }

    protected final HttpMetadata buildMetadata(
            int statusCode, Object data, Throwable throwable, HttpOutputMessage message) {
        HttpMetadata metadata = encodeHttpMetadata(message == null);
        HttpHeaders headers = metadata.headers();
        headers.set(HttpHeaderNames.STATUS.getKey(), HttpUtils.toStatusString(statusCode));
        if (message != null) {
            headers.set(HttpHeaderNames.CONTENT_TYPE.getKey(), responseEncoder.contentType());
        }
        customizeHeaders(headers, throwable, message);
        if (data instanceof HttpResult) {
            HttpResult<?> result = (HttpResult<?>) data;
            if (result.getHeaders() != null) {
                headers.set(result.getHeaders());
            }
        }
        return metadata;
    }

    protected abstract HttpMetadata encodeHttpMetadata(boolean endStream);

    protected void customizeHeaders(HttpHeaders headers, Throwable throwable, HttpOutputMessage message) {
        List<BiConsumer<HttpHeaders, Throwable>> headersCustomizers = this.headersCustomizers;
        if (headersCustomizers != null) {
            for (int i = 0, size = headersCustomizers.size(); i < size; i++) {
                headersCustomizers.get(i).accept(headers, throwable);
            }
        }
    }

    protected final void sendMetadata(HttpMetadata metadata) {
        if (headerSent) {
            return;
        }
        getHttpChannel().writeHeader(metadata);
        headerSent = true;
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Http response headers sent: " + metadata.headers());
        }
    }