reactor.ipc.netty.http.client.HttpClient.java Source code

Java tutorial

Introduction

Here is the source code for reactor.ipc.netty.http.client.HttpClient.java

Source

/*
 * Copyright (c) 2011-2016 Pivotal Software Inc, All Rights Reserved.
 *
 * 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 reactor.ipc.netty.http.client;

import java.net.InetSocketAddress;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

import io.netty.channel.Channel;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.pool.ChannelPool;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequestEncoder;
import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.logging.LoggingHandler;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
import reactor.core.publisher.MonoSink;
import reactor.ipc.netty.NettyConnector;
import reactor.ipc.netty.NettyContext;
import reactor.ipc.netty.NettyInbound;
import reactor.ipc.netty.NettyOutbound;
import reactor.ipc.netty.NettyPipeline;
import reactor.ipc.netty.channel.ContextHandler;
import reactor.ipc.netty.http.HttpResources;
import reactor.ipc.netty.http.server.HttpServerResponse;
import reactor.ipc.netty.options.ClientOptions;
import reactor.ipc.netty.tcp.TcpClient;

/**
 * The base class for a Netty-based Http client.
 *
 * @author Stephane Maldini
 */
public class HttpClient implements NettyConnector<HttpClientResponse, HttpClientRequest> {

    /**
     * @return a simple HTTP client
     */
    public static HttpClient create() {
        return create(HttpClientOptions::sslSupport);
    }

    /**
     * @return a simple HTTP client
     */
    public static HttpClient create(Consumer<? super HttpClientOptions> options) {
        Objects.requireNonNull(options, "options");
        HttpClientOptions clientOptions = HttpClientOptions.create();
        clientOptions.loopResources(HttpResources.get()).poolResources(HttpResources.get());
        options.accept(clientOptions);
        return new HttpClient(clientOptions.duplicate());
    }

    /**
     * @return a simple HTTP client
     */
    public static HttpClient create(String address) {
        return create(address, 80);
    }

    /**
     * @return a simple HTTP client
     */
    public static HttpClient create(String address, int port) {
        return create(opts -> opts.connect(address, port));
    }

    /**
     * @return a simple HTTP client
     */
    public static HttpClient create(int port) {
        return create("localhost", port);
    }

    final TcpBridgeClient client;
    final HttpClientOptions options;

    protected HttpClient(final HttpClientOptions options) {
        this.client = new TcpBridgeClient(options);
        this.options = options;
    }

    /**
     * HTTP DELETE the passed URL. When connection has been made, the passed handler is
     * invoked and can be used to build precisely the request and write data to it.
     *
     * @param url the target remote URL
     * @param handler the {@link Function} to invoke on open channel
     *
     * @return a {@link Mono} of the {@link HttpServerResponse} ready to consume for
     * response
     */
    public final Mono<HttpClientResponse> delete(String url,
            Function<? super HttpClientRequest, ? extends Publisher<Void>> handler) {
        return request(HttpMethod.DELETE, url, handler);
    }

    /**
     * HTTP DELETE the passed URL. When connection has been made, the passed handler is
     * invoked and can be used to build precisely the request and write data to it.
     *
     * @param url the target remote URL
     *
     * @return a {@link Mono} of the {@link HttpServerResponse} ready to consume for
     * response
     */
    public final Mono<HttpClientResponse> delete(String url) {
        return request(HttpMethod.DELETE, url, null);
    }

    /**
     * HTTP GET the passed URL. When connection has been made, the passed handler is
     * invoked and can be used to build precisely the request and write data to it.
     *
     * @param url the target remote URL
     * @param handler the {@link Function} to invoke on open channel
     *
     * @return a {@link Mono} of the {@link HttpServerResponse} ready to consume for
     * response
     */
    public final Mono<HttpClientResponse> get(String url,
            Function<? super HttpClientRequest, ? extends Publisher<Void>> handler) {
        return request(HttpMethod.GET, url, handler);
    }

    /**
     * HTTP GET the passed URL.
     *
     * @param url the target remote URL
     *
     * @return a {@link Mono} of the {@link HttpServerResponse} ready to consume for
     * response
     */
    public final Mono<HttpClientResponse> get(String url) {
        return request(HttpMethod.GET, url, null);
    }

    @Override
    @SuppressWarnings("unchecked")
    public Mono<HttpClientResponse> newHandler(
            BiFunction<? super HttpClientResponse, ? super HttpClientRequest, ? extends Publisher<Void>> ioHandler) {
        return (Mono<HttpClientResponse>) client
                .newHandler((BiFunction<NettyInbound, NettyOutbound, Publisher<Void>>) ioHandler);
    }

    /**
     * HTTP PATCH the passed URL. When connection has been made, the passed handler is
     * invoked and can be used to build precisely the request and write data to it.
     *
     * @param url the target remote URL
     * @param handler the {@link Function} to invoke on open channel
     *
     * @return a {@link Mono} of the {@link HttpServerResponse} ready to consume for
     * response
     */
    public final Mono<HttpClientResponse> patch(String url,
            Function<? super HttpClientRequest, ? extends Publisher<Void>> handler) {
        return request(HttpMethod.PATCH, url, handler);
    }

    /**
     * HTTP PATCH the passed URL.
     *
     * @param url the target remote URL
     *
     * @return a {@link Mono} of the {@link HttpServerResponse} ready to consume for
     * response
     */
    public final Mono<HttpClientResponse> patch(String url) {
        return request(HttpMethod.PATCH, url, null);
    }

    /**
     * HTTP POST the passed URL. When connection has been made, the passed handler is
     * invoked and can be used to build precisely the request and write data to it.
     *
     * @param url the target remote URL
     * @param handler the {@link Function} to invoke on open channel
     *
     * @return a {@link Mono} of the {@link HttpServerResponse} ready to consume for
     * response
     */
    public final Mono<HttpClientResponse> post(String url,
            Function<? super HttpClientRequest, ? extends Publisher<Void>> handler) {
        return request(HttpMethod.POST, url, handler);
    }

    /**
     * HTTP PUT the passed URL. When connection has been made, the passed handler is
     * invoked and can be used to build precisely the request and write data to it.
     *
     * @param url the target remote URL
     * @param handler the {@link Function} to invoke on open channel
     *
     * @return a {@link Mono} of the {@link HttpServerResponse} ready to consume for
     * response
     */
    public final Mono<HttpClientResponse> put(String url,
            Function<? super HttpClientRequest, ? extends Publisher<Void>> handler) {
        return request(HttpMethod.PUT, url, handler);
    }

    /**
     * Use the passed HTTP method to send to the given URL. When connection has been made,
     * the passed handler is invoked and can be used to build precisely the request and
     * write data to it.
     *
     * @param method the HTTP method to send
     * @param url the target remote URL
     * @param handler the {@link Function} to invoke on opened TCP connection
     *
     * @return a {@link Mono} of the {@link HttpServerResponse} ready to consume for
     * response
     */
    public Mono<HttpClientResponse> request(HttpMethod method, String url,
            Function<? super HttpClientRequest, ? extends Publisher<Void>> handler) {

        if (method == null || url == null) {
            throw new IllegalArgumentException("Method && url cannot be both null");
        }

        return new MonoHttpClientResponse(this, url, method, handler);
    }

    /**
     * WebSocket to the passed URL.
     *
     * @param url the target remote URL
     *
     * @return a {@link Mono} of the {@link HttpServerResponse} ready to consume for
     * response
     */
    public final Mono<HttpClientResponse> ws(String url) {
        return request(WS, url, HttpClientRequest::sendWebsocket);
    }

    /**
     * WebSocket to the passed URL.
     *
     * @param url the target remote URL
     * @param headerBuilder the  header {@link Consumer} to invoke before sending websocket
     * handshake
     *
     * @return a {@link Mono} of the {@link HttpServerResponse} ready to consume for
     * response
     */
    public final Mono<HttpClientResponse> ws(String url, final Consumer<? super HttpHeaders> headerBuilder) {
        return request(WS, url, ch -> {
            headerBuilder.accept(ch.requestHeaders());
            return ch.sendWebsocket();
        });
    }

    static final HttpMethod WS = new HttpMethod("WS");
    final static String WS_SCHEME = "ws";
    final static String WSS_SCHEME = "wss";
    final static String HTTP_SCHEME = "http";
    final static String HTTPS_SCHEME = "https";
    final static LoggingHandler loggingHandler = new LoggingHandler(HttpClient.class);

    @SuppressWarnings("unchecked")
    final class TcpBridgeClient extends TcpClient implements BiConsumer<ChannelPipeline, ContextHandler<Channel>> {

        TcpBridgeClient(ClientOptions options) {
            super(options);
        }

        @Override
        protected Mono<NettyContext> newHandler(
                BiFunction<? super NettyInbound, ? super NettyOutbound, ? extends Publisher<Void>> handler,
                InetSocketAddress address, boolean secure, Consumer<? super Channel> onSetup) {
            return super.newHandler(handler, address, secure, onSetup);
        }

        @Override
        protected ContextHandler<SocketChannel> doHandler(
                BiFunction<? super NettyInbound, ? super NettyOutbound, ? extends Publisher<Void>> handler,
                MonoSink<NettyContext> sink, boolean secure, ChannelPool pool, Consumer<? super Channel> onSetup) {
            return ContextHandler
                    .<SocketChannel>newClientContext(sink, options, loggingHandler, secure, pool, (ch, c, msg) -> {
                        if (onSetup != null) {
                            onSetup.accept(ch);
                        }
                        return HttpClientOperations.bindHttp(ch, handler, c);
                    }).onPipeline(this);
        }

        @Override
        public void accept(ChannelPipeline pipeline, ContextHandler<Channel> c) {
            pipeline.addLast(NettyPipeline.HttpDecoder, new HttpResponseDecoder())
                    .addLast(NettyPipeline.HttpEncoder, new HttpRequestEncoder());
        }
    }

}