All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.gravitee.gateway.reactive.http.vertx.VertxHttpServerResponse Maven / Gradle / Ivy

/*
 * Copyright © 2015 The Gravitee team (http://gravitee.io)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.gravitee.gateway.reactive.http.vertx;

import io.gravitee.common.http.HttpHeadersValues;
import io.gravitee.common.http.HttpVersion;
import io.gravitee.gateway.http.utils.RequestUtils;
import io.gravitee.gateway.http.vertx.VertxHttpHeaders;
import io.gravitee.gateway.reactive.api.context.GenericExecutionContext;
import io.gravitee.gateway.reactive.api.message.Message;
import io.gravitee.gateway.reactive.core.context.AbstractResponse;
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.Flowable;
import io.vertx.core.http.HttpHeaders;
import io.vertx.rxjava3.core.http.HttpServerResponse;
import java.util.concurrent.atomic.AtomicReference;
import org.reactivestreams.Subscription;

/**
 * @author Guillaume LAMIRAND (guillaume.lamirand at graviteesource.com)
 * @author GraviteeSource Team
 */
public class VertxHttpServerResponse extends AbstractResponse {

    protected final HttpServerResponse nativeResponse;
    private final VertxHttpServerRequest vertxHttpServerRequest;
    private Boolean isStreaming = null;

    public VertxHttpServerResponse(final VertxHttpServerRequest vertxHttpServerRequest) {
        this.nativeResponse = vertxHttpServerRequest.nativeRequest.response();
        this.vertxHttpServerRequest = vertxHttpServerRequest;
        this.headers = new VertxHttpHeaders(nativeResponse.headers().getDelegate());
        this.trailers = new VertxHttpHeaders(nativeResponse.trailers().getDelegate());
    }

    public HttpServerResponse getNativeResponse() {
        return nativeResponse;
    }

    public boolean opened() {
        return !nativeResponse.closed() && !nativeResponse.ended();
    }

    @Override
    public int status() {
        return nativeResponse.getStatusCode();
    }

    @Override
    public String reason() {
        return nativeResponse.getStatusMessage();
    }

    @Override
    public VertxHttpServerResponse reason(String reason) {
        if (reason != null) {
            nativeResponse.setStatusMessage(reason);
        }
        return this;
    }

    @Override
    public VertxHttpServerResponse status(int statusCode) {
        nativeResponse.setStatusCode(statusCode);
        return this;
    }

    @Override
    public boolean ended() {
        return nativeResponse.ended();
    }

    @Override
    public Completable end(final GenericExecutionContext ctx) {
        return Completable.defer(() -> {
            if (vertxHttpServerRequest.isWebSocketUpgraded()) {
                return chunks().ignoreElements();
            }

            if (!opened()) {
                return Completable.error(new IllegalStateException("The response is already ended"));
            }
            prepareHeaders();
            // this atomic reference maybe useless due to  https://github.com/vert-x3/vertx-rx/pull/285
            // how to confirm ?
            final AtomicReference subscriptionRef = new AtomicReference<>();

            if (lazyBufferFlow().hasChunks()) {
                return nativeResponse
                    .rxSend(
                        chunks()
                            .doOnSubscribe(subscriptionRef::set)
                            .map(buffer -> io.vertx.rxjava3.core.buffer.Buffer.buffer(buffer.getNativeBuffer()))
                            .doOnNext(buffer ->
                                ctx.metrics().setResponseContentLength(ctx.metrics().getResponseContentLength() + buffer.length())
                            )
                    )
                    .doOnDispose(() -> subscriptionRef.get().cancel());
            }

            return nativeResponse.rxEnd();
        });
    }

    @Override
    public void messages(final Flowable messages) {
        super.messages(messages);

        // If message flow is set up, make sure any access to chunk buffers will not be possible anymore and returns empty.
        chunks(Flowable.empty());
    }

    protected void prepareHeaders() {
        if (!nativeResponse.headWritten()) {
            if (HttpVersion.HTTP_2 == vertxHttpServerRequest.version()) {
                if (
                    headers.contains(io.vertx.core.http.HttpHeaders.CONNECTION) &&
                    headers.getAll(io.vertx.core.http.HttpHeaders.CONNECTION).contains(HttpHeadersValues.CONNECTION_GO_AWAY)
                ) {
                    // 'Connection: goAway' is a special header indicating the native connection should be shutdown because of the node itself will shutdown.
                    vertxHttpServerRequest.nativeRequest.connection().shutdown();
                }

                // As per https://tools.ietf.org/html/rfc7540#section-8.1.2.2
                // connection-specific header fields must be removed from response headers
                headers
                    .remove(io.vertx.core.http.HttpHeaders.CONNECTION)
                    .remove(io.vertx.core.http.HttpHeaders.KEEP_ALIVE)
                    .remove(io.vertx.core.http.HttpHeaders.TRANSFER_ENCODING);
            }

            if (headers.contains(HttpHeaders.CONTENT_LENGTH)) {
                headers.remove(HttpHeaders.TRANSFER_ENCODING);
            }
        }
    }

    @Override
    public boolean isStreaming() {
        if (isStreaming == null) {
            isStreaming = RequestUtils.isStreaming(this.vertxHttpServerRequest, this);
        }
        return isStreaming;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy