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

io.reactivex.netty.protocol.http.client.internal.HttpClientRequestImpl Maven / Gradle / Ivy

There is a newer version: 0.5.3-rc.2
Show newest version
/*
 * Copyright 2016 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.protocol.http.client.internal;

import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.cookie.Cookie;
import io.reactivex.netty.channel.AllocatingTransformer;
import io.reactivex.netty.channel.AppendTransformerEvent;
import io.reactivex.netty.channel.Connection;
import io.reactivex.netty.events.Clock;
import io.reactivex.netty.events.EventAttributeKeys;
import io.reactivex.netty.events.EventPublisher;
import io.reactivex.netty.internal.VoidToAnythingCast;
import io.reactivex.netty.protocol.http.TrailingHeaders;
import io.reactivex.netty.protocol.http.client.HttpClientRequest;
import io.reactivex.netty.protocol.http.client.HttpClientResponse;
import io.reactivex.netty.protocol.http.client.events.HttpClientEventsListener;
import io.reactivex.netty.protocol.http.internal.OperatorTrailer;
import io.reactivex.netty.protocol.http.ws.client.internal.WebSocketRequestImpl;
import io.reactivex.netty.protocol.tcp.client.TcpClient;
import rx.Observable;
import rx.Subscriber;
import rx.functions.Func0;
import rx.functions.Func1;
import rx.functions.Func2;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static java.util.concurrent.TimeUnit.*;

public final class HttpClientRequestImpl extends HttpClientRequest {

    public static final int NO_REDIRECTS = -1;

    private final List immutableTransformers;
    private final List immutableResponseTransformers;
    private final RawRequest rawRequest;
    private final TcpClient> client;
    private final Func1 flushOnEachSelector = new Func1() {
        @Override
        public Boolean call(I next) {
            return true;
        }
    };

    private HttpClientRequestImpl(final RawRequest rawRequest, final TcpClient> client,
                                  List immutableTransformers,
                                  List immutableResponseTransformers) {
        super(new OnSubscribeFuncImpl<>(client, rawRequest, immutableResponseTransformers, immutableTransformers));
        this.rawRequest = rawRequest;
        this.client = client;
        this.immutableTransformers = immutableTransformers;
        this.immutableResponseTransformers = immutableResponseTransformers;
    }

    @Override
    public Observable> writeContent(Observable contentSource) {
        @SuppressWarnings("rawtypes")
        Observable rawObservable = contentSource;
        return _writeContentRaw(rawObservable, false);
    }

    @Override
    public Observable> writeContentAndFlushOnEach(Observable contentSource) {
        return writeContent(contentSource, flushOnEachSelector);
    }

    @Override
    public Observable> writeStringContent(Observable contentSource) {
        @SuppressWarnings("rawtypes")
        Observable rawObservable = contentSource;
        return _writeContentRaw(rawObservable, false);
    }

    @Override
    public Observable> writeBytesContent(Observable contentSource) {
        @SuppressWarnings("rawtypes")
        Observable rawObservable = contentSource;
        return _writeContentRaw(rawObservable, false);
    }

    @Override
    public Observable> writeContent(Observable contentSource,
                                                          Func1 flushSelector) {
        @SuppressWarnings("rawtypes")
        Observable rawObservable = contentSource;
        return _writeContentRaw(rawObservable, flushSelector, false);
    }

    @Override
    public Observable> writeStringContent(Observable contentSource,
                                                                Func1 flushSelector) {
        @SuppressWarnings("rawtypes")
        Observable rawObservable = contentSource;
        return _writeContentRaw(rawObservable, flushSelector, false);
    }

    @Override
    public Observable> writeBytesContent(Observable contentSource,
                                                               Func1 flushSelector) {
        @SuppressWarnings("rawtypes")
        Observable rawObservable = contentSource;
        return _writeContentRaw(rawObservable, flushSelector, false);
    }

    @Override
    public  Observable> writeContent(Observable contentSource,
                                                                              final Func0 trailerFactory,
                                                                              final Func2 trailerMutator) {
        @SuppressWarnings("rawtypes")
        Observable rawObservable = contentSource;
        return _writeContentRaw(OperatorTrailer.liftFrom(rawObservable, trailerFactory, trailerMutator), true);
    }

    @Override
    public  Observable> writeStringContent(Observable contentSource,
                                                                                    Func0 trailerFactory,
                                                                                    Func2 trailerMutator) {
        @SuppressWarnings("rawtypes")
        Observable rawObservable = contentSource;
        return _writeContentRaw(OperatorTrailer.liftFrom(rawObservable, trailerFactory, trailerMutator), true);
    }

    @Override
    public  Observable> writeBytesContent(Observable contentSource,
                                                                                   Func0 trailerFactory,
                                                                                   Func2 trailerMutator) {
        @SuppressWarnings("rawtypes")
        Observable rawObservable = contentSource;
        return _writeContentRaw(OperatorTrailer.liftFrom(rawObservable, trailerFactory, trailerMutator), true);
    }

    @Override
    public  Observable> writeContent(Observable contentSource,
                                                                                      Func0 trailerFactory,
                                                                                      Func2 trailerMutator,
                                                                                      Func1 flushSelector) {
        @SuppressWarnings("rawtypes")
        Observable rawObservable = contentSource;
        return _writeContentRaw(OperatorTrailer.liftFrom(rawObservable, trailerFactory, trailerMutator), flushSelector,
                                true);
    }

    @Override
    public  Observable> writeStringContent(
            Observable contentSource, Func0 trailerFactory, Func2 trailerMutator,
            Func1 flushSelector) {
        @SuppressWarnings("rawtypes")
        Observable rawObservable = contentSource;
        return _writeContentRaw(OperatorTrailer.liftFrom(rawObservable, trailerFactory, trailerMutator), flushSelector,
                                true);
    }

    @Override
    public  Observable> writeBytesContent(
            Observable contentSource, Func0 trailerFactory, Func2 trailerMutator,
            Func1 flushSelector) {
        @SuppressWarnings("rawtypes")
        Observable rawObservable = contentSource;
        return _writeContentRaw(OperatorTrailer.liftFrom(rawObservable, trailerFactory, trailerMutator), flushSelector,
                                true);
    }

    @Override
    public HttpClientRequestImpl readTimeOut(int timeOut, TimeUnit timeUnit) {
        return _copy(client.readTimeOut(timeOut, timeUnit));
    }

    @Override
    public HttpClientRequestImpl followRedirects(int maxRedirects) {
        final Redirector redirector = new Redirector<>(maxRedirects, client);
        HttpClientRequestImpl toReturn = _copy(client, rawRequest.followRedirect(redirector));
        redirector.setOriginalRequest(toReturn.rawRequest);
        return toReturn;
    }

    @Override
    public HttpClientRequestImpl followRedirects(boolean follow) {
        return follow ? followRedirects(Redirector.DEFAULT_MAX_REDIRECTS) : followRedirects(NO_REDIRECTS);
    }

    @Override
    public HttpClientRequestImpl setMethod(HttpMethod method) {
        return _copy(client, rawRequest.setMethod(method));
    }

    @Override
    public HttpClientRequestImpl setUri(String newUri) {
        return _copy(client, rawRequest.setUri(newUri));
    }

    @Override
    public HttpClientRequestImpl addHeader(CharSequence name, Object value) {
        return _copy(client, rawRequest.addHeader(name, value));
    }

    @Override
    public HttpClientRequest addHeaders(Map> headers) {
        return _copy(client, rawRequest.addHeaders(headers));
    }

    @Override
    public HttpClientRequestImpl addCookie(Cookie cookie) {
        return _copy(client, rawRequest.addCookie(cookie));
    }

    @Override
    public HttpClientRequestImpl addDateHeader(CharSequence name, Date value) {
        return _copy(client, rawRequest.addDateHeader(name, value));
    }

    @Override
    public HttpClientRequestImpl addDateHeader(CharSequence name, Iterable values) {
        return _copy(client, rawRequest.addDateHeader(name, values));
    }

    @Override
    public HttpClientRequestImpl addHeaderValues(CharSequence name, Iterable values) {
        return _copy(client, rawRequest.addHeaderValues(name, values));
    }

    @Override
    public HttpClientRequestImpl setDateHeader(CharSequence name, Date value) {
        return _copy(client, rawRequest.setDateHeader(name, value));
    }

    @Override
    public HttpClientRequestImpl setHeader(CharSequence name, Object value) {
        return _copy(client, rawRequest.setHeader(name, value));
    }

    @Override
    public HttpClientRequest setHeaders(Map> headers) {
        return _copy(client, rawRequest.setHeaders(headers));
    }

    @Override
    public HttpClientRequestImpl setDateHeader(CharSequence name, Iterable values) {
        return _copy(client, rawRequest.setDateHeader(name, values));
    }

    @Override
    public HttpClientRequestImpl setHeaderValues(CharSequence name, Iterable values) {
        return _copy(client, rawRequest.setHeaderValues(name, values));
    }

    @Override
    public HttpClientRequestImpl removeHeader(CharSequence name) {
        return _copy(client, rawRequest.removeHeader(name));
    }

    @Override
    public HttpClientRequestImpl setKeepAlive(boolean keepAlive) {
        return _copy(client, rawRequest.setKeepAlive(keepAlive));
    }

    @Override
    public HttpClientRequestImpl setTransferEncodingChunked() {
        return _copy(client, rawRequest.setTransferEncodingChunked());
    }

    @Override
    public  HttpClientRequestImpl transformContent(AllocatingTransformer transformer) {
        final List newTransformers = new ArrayList<>(immutableTransformers);
        @SuppressWarnings("unchecked")
        AppendTransformerEvent e = new AppendTransformerEvent(transformer);
        newTransformers.add(e);
        @SuppressWarnings("unchecked")
        RawRequest cast = (RawRequest) this.rawRequest;
        return new HttpClientRequestImpl<>(cast, client, newTransformers, immutableResponseTransformers);
    }

    @Override
    public  HttpClientRequestImpl transformResponseContent(Transformer transformer) {
        final List newTransformers = new ArrayList<>(immutableResponseTransformers);
        newTransformers.add(transformer);
        @SuppressWarnings("unchecked")
        RawRequest cast = (RawRequest) this.rawRequest;
        TcpClient rawClient = client;
        @SuppressWarnings("unchecked")
        TcpClient> _client = (TcpClient>)rawClient;
        return new HttpClientRequestImpl<>(cast, _client, immutableTransformers, newTransformers);
    }

    @Override
    public WebSocketRequestImpl requestWebSocketUpgrade() {
        return WebSocketRequestImpl.createNew(this);
    }

    @Override
    public boolean containsHeader(CharSequence name) {
        return rawRequest.getHeaders().headers().contains(name);
    }

    @Override
    public boolean containsHeaderWithValue(CharSequence name, CharSequence value, boolean caseInsensitiveValueMatch) {
        return rawRequest.getHeaders().headers().contains(name, value, caseInsensitiveValueMatch);
    }

    @Override
    public String getHeader(CharSequence name) {
        return rawRequest.getHeaders().headers().get(name);
    }

    @Override
    public List getAllHeaders(CharSequence name) {
        return rawRequest.getHeaders().headers().getAll(name);
    }

    @Override
    public Iterator> headerIterator() {
        return rawRequest.getHeaders().headers().iteratorCharSequence();
    }

    @Override
    public Set getHeaderNames() {
        return rawRequest.getHeaders().headers().names();
    }

    @Override
    public HttpVersion getHttpVersion() {
        return rawRequest.getHeaders().protocolVersion();
    }

    @Override
    public HttpMethod getMethod() {
        return rawRequest.getHeaders().method();
    }

    @Override
    public String getUri() {
        return rawRequest.getHeaders().uri();
    }

    public static  HttpClientRequestImpl create(final HttpVersion version, final HttpMethod httpMethod,
                                                        final String uri,
                                                        final TcpClient> client,
                                                        int maxRedirects) {
        Redirector redirector = NO_REDIRECTS == maxRedirects
                                                                ? null
                                                                : new Redirector(maxRedirects, client
        );

        final RawRequest rawRequest = RawRequest.create(version, httpMethod, uri, redirector);

        if (null != redirector) {
            redirector.setOriginalRequest(rawRequest);
        }

        return create(rawRequest, client);
    }

    public static  HttpClientRequestImpl create(final HttpVersion version, final HttpMethod httpMethod,
                                                        final String uri,
                                                        final TcpClient> client) {
        return create(version, httpMethod, uri, client, NO_REDIRECTS);
    }

    public static  HttpClientRequestImpl create(final RawRequest rawRequest,
                                                        final TcpClient> client) {
        return new HttpClientRequestImpl<>(rawRequest, client, Collections.emptyList(),
                                           Collections.emptyList());
    }

    public TcpClient> getClient() {
        return client;
    }

    @SuppressWarnings("unchecked")
    private  HttpClientRequestImpl _copy(TcpClient> c) {
        return _copy(c, (RawRequest)rawRequest);
    }

    @SuppressWarnings("unchecked")
    private  HttpClientRequestImpl _copy(TcpClient> c,
                                                     RawRequest rawRequest) {
        return new HttpClientRequestImpl<>(rawRequest, c, immutableTransformers, immutableResponseTransformers);
    }

    @SuppressWarnings("rawtypes")
    private Observable> _writeContentRaw(Observable rawContent, boolean hasTrailers) {
        return _writeContentRaw(rawContent, null, hasTrailers);
    }

    @SuppressWarnings("rawtypes")
    private Observable> _writeContentRaw(Observable rawContent,
                                                               Func1 flushSelector, boolean hasTrailers) {
        final RawRequest r = RawRequest.create(rawRequest.getHeaders(), rawContent, flushSelector, hasTrailers,
                                                     rawRequest.getRedirector());
        return new HttpClientRequestImpl<>(r, client, immutableTransformers, immutableResponseTransformers);
    }

    public RawRequest unsafeRawRequest() {
        return rawRequest;
    }

    private static class OnSubscribeFuncImpl implements OnSubscribe> {
        @SuppressWarnings("rawtypes")
        private final Observable source;
        private final TcpClient> client;

        public OnSubscribeFuncImpl(final TcpClient> client, RawRequest rawRequest,
                                   List responseTransformers,
                                   List requestTransformers) {
            this.client = client;
            ConnToResponseFunc connToResponseFunc = new ConnToResponseFunc<>(rawRequest, responseTransformers,
                                                                                   requestTransformers);
            Observable> source = this.client.createConnectionRequest()
                                                                  .take(1)
                                                                  .switchMap(connToResponseFunc);

            if (null != rawRequest.getRedirector()) {
                source = source.switchMap(rawRequest.getRedirector());
            }

            this.source = source;
        }

        @Override
        @SuppressWarnings("unchecked")
        public void call(Subscriber> subscriber) {
            @SuppressWarnings("rawtypes")
            final Subscriber rawSub = subscriber;
            source.unsafeSubscribe(rawSub);
        }

    }

    private static class ConnToResponseFunc
            implements Func1, ?>, Observable>> {

        private final RawRequest rawRequest;
        private List responseTransformers;
        private List requestTransformers;

        public ConnToResponseFunc(RawRequest rawRequest, List responseTransformers,
                                  List requestTransformers) {
            this.rawRequest = rawRequest;
            this.responseTransformers = responseTransformers;
            this.requestTransformers = requestTransformers;
        }

        @Override
        public Observable> call(final Connection, ?> conn) {
            for (AppendTransformerEvent requestTransformer : requestTransformers) {
                conn.unsafeNettyChannel().pipeline().fireUserEventTriggered(requestTransformer);
            }

            final Observable> input = conn.getInput();

            final HttpClientEventsListener eventsListener =
                    conn.unsafeNettyChannel().attr(HttpChannelProvider.HTTP_CLIENT_EVENT_LISTENER).get();
            final EventPublisher eventPublisher =
                    conn.unsafeNettyChannel().attr(EventAttributeKeys.EVENT_PUBLISHER).get();

            return writeRequest(conn).lift(new RequestWriteMetricsOperator(eventsListener, eventPublisher))
                                     .map(new VoidToAnythingCast>())
                                     .ignoreElements()
                                     .concatWith(input.take(1))
                                     .map(new Func1, HttpClientResponse>() {
                                         @SuppressWarnings("unchecked")
                                         @Override
                                         public HttpClientResponse call(HttpClientResponse r) {
                                             HttpClientResponse rp = HttpClientResponseImpl.newInstance(r, conn);
                                             for (Transformer transformer : responseTransformers) {
                                                 rp = rp.transformContent(transformer);
                                             }
                                             return (HttpClientResponse) rp;
                                         }
                                     });
        }

        @SuppressWarnings("unchecked")
        protected Observable writeRequest(Connection, ?> conn) {
            return conn.write(rawRequest.asObservable(conn));
        }
    }

    private static class RequestWriteMetricsOperator implements Operator {

        private final EventPublisher eventPublisher;
        private final HttpClientEventsListener eventsListener;

        public RequestWriteMetricsOperator(HttpClientEventsListener eventsListener, EventPublisher eventPublisher) {
            this.eventPublisher = eventPublisher;
            this.eventsListener = eventsListener;
        }

        @Override
        public Subscriber call(final Subscriber o) {
            final long startTimeNanos = eventPublisher.publishingEnabled() ? Clock.newStartTimeNanos() : -1;
            if (eventPublisher.publishingEnabled()) {
                eventsListener.onRequestSubmitted();
            }
            return new Subscriber(o) {
                @Override
                public void onCompleted() {
                    if (eventPublisher.publishingEnabled()) {
                        eventsListener.onRequestWriteComplete(Clock.onEndNanos(startTimeNanos), NANOSECONDS);
                    }
                    o.onCompleted();
                }

                @Override
                public void onError(Throwable e) {
                    if (eventPublisher.publishingEnabled()) {
                        eventsListener.onRequestWriteFailed(Clock.onEndNanos(startTimeNanos), NANOSECONDS, e);
                    }
                    o.onError(e);
                }

                @Override
                public void onNext(Void aVoid) {
                    o.onNext(aVoid);
                }
            };
        }
    }
}