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

io.reactivex.netty.contexts.http.HttpContextClientChannelFactory Maven / Gradle / Ivy

There is a newer version: 0.5.1
Show newest version
/*
 * Copyright 2014 Netflix, Inc.
 *
 * 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.reactivex.netty.contexts.http;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.reactivex.netty.channel.ObservableConnection;
import io.reactivex.netty.client.ClientChannelFactoryImpl;
import io.reactivex.netty.client.ClientConnectionFactory;
import io.reactivex.netty.client.ClientMetricsEvent;
import io.reactivex.netty.client.RxClient;
import io.reactivex.netty.contexts.ContextsContainer;
import io.reactivex.netty.contexts.RequestCorrelator;
import io.reactivex.netty.metrics.MetricEventsSubject;
import io.reactivex.netty.protocol.http.client.HttpClientRequest;
import io.reactivex.netty.protocol.http.client.HttpClientResponse;
import rx.Subscriber;

import static io.reactivex.netty.contexts.AbstractClientContextHandler.NewContextEvent;

/**
 * @author Nitesh Kant
 */
public class HttpContextClientChannelFactory extends
        ClientChannelFactoryImpl, HttpClientRequest> {

    private final RequestCorrelator correlator;

    public HttpContextClientChannelFactory(Bootstrap clientBootstrap, RequestCorrelator correlator,
                                           MetricEventsSubject> eventsSubject) {
        super(clientBootstrap, eventsSubject);
        this.correlator = correlator;
    }

    @Override
    public ChannelFuture connect(
            Subscriber, HttpClientRequest>> subscriber,
            RxClient.ServerInfo serverInfo,
            ClientConnectionFactory, HttpClientRequest,
                    ? extends ObservableConnection, HttpClientRequest>> connectionFactory) {
        final ContextCapturingSubscriber capturingSubscriber = new ContextCapturingSubscriber(subscriber);
        return super.connect(capturingSubscriber, serverInfo, connectionFactory);
    }

    @Override
    public void onNewConnection(ObservableConnection, HttpClientRequest> newConnection,
                                Subscriber, HttpClientRequest>> subscriber) {
        /*
         * This will either be called after a call to connect() or directly (from pool).
         * If it is the former then the subscriber should already be a capturing sub. In case of latter, we should be
         * called from the thread that has the relevant state & hence we capture the context now.
         */
        final Subscriber, HttpClientRequest>> subToUse;
        if (ContextCapturingSubscriber.class == subscriber.getClass()) {
            subToUse = subscriber;
        } else {
            subToUse = new ContextCapturingSubscriber(subscriber);
        }

        super.onNewConnection(newConnection, subToUse);
    }

    private class ContextCapturingSubscriber extends Subscriber, HttpClientRequest>> {

        private final Subscriber, HttpClientRequest>> original;
        private final String requestId;
        private final ContextsContainer container;

        private ContextCapturingSubscriber(Subscriber, HttpClientRequest>> original) {
            super(original);
            this.original = original;
            requestId = correlator.getRequestIdForClientRequest();
            container = correlator.getContextForClientRequest(requestId);
        }

        @Override
        public void onCompleted() {
            original.onCompleted();
        }

        @Override
        public void onError(Throwable e) {
            original.onError(e);
        }

        @Override
        public void onNext(ObservableConnection, HttpClientRequest> connection) {
            if (null != requestId && null != container) {
                connection.getChannel().pipeline()
                          .fireUserEventTriggered(new NewContextEvent(requestId, container));
            }
            original.onNext(connection);
        }
    }
}