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

reactor.io.net.http.HttpServer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2011-2015 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.io.net.http;

import org.reactivestreams.Publisher;
import reactor.Environment;
import reactor.bus.registry.Registration;
import reactor.bus.registry.Registries;
import reactor.bus.registry.Registry;
import reactor.bus.selector.Selector;
import reactor.core.Dispatcher;
import reactor.io.buffer.Buffer;
import reactor.io.codec.Codec;
import reactor.io.net.NetSelectors;
import reactor.io.net.ReactorChannelHandler;
import reactor.io.net.ReactorPeer;
import reactor.io.net.http.model.HttpHeaders;
import reactor.rx.Promise;

import java.net.InetSocketAddress;
import java.util.Iterator;
import java.util.List;

/**
 * Base functionality needed by all servers that communicate with clients over HTTP.
 *
 * @param   The type that will be received by this server
 * @param  The type that will be sent by this server
 *
 * @author Stephane Maldini
 */
public abstract class HttpServer
		extends ReactorPeer> {

	protected final Registry>> routedWriters;

	private boolean hasWebsocketEndpoints = false;

	protected HttpServer(Environment env, Dispatcher dispatcher, Codec codec) {
		super(env, dispatcher, codec);
		this.routedWriters = Registries.create();
	}

	/**
	 * Start the server without any global handler, only the specific routed methods (get, post...) will apply.
	 *
	 * @return a Promise fulfilled when server is started
	 */
	public Promise start() {
		return start(null);
	}

	/**
	 * Get the address to which this server is bound. If port 0 was used on configuration, try resolving the port.
	 *
	 * @return the bind address
	 */
	public abstract InetSocketAddress getListenAddress();

	/**
	 * Register an handler for the given Selector condition, incoming connections will query the internal registry
	 *  to invoke the matching handlers. Implementation may choose to reply 404 if no route matches.
	 *
	 * @param condition a {@link Selector} to match the incoming connection with registered handler
	 * @param serviceFunction an handler to invoke for the given condition
	 *
	 * @return {@code this}
	 */
	@SuppressWarnings("unchecked")
	public HttpServer route(
			final Selector condition,
			final ReactorChannelHandler> serviceFunction) {

		routedWriters.register(condition, serviceFunction);
		return this;
	}

	/**
	 * Listen for HTTP GET on the passed path to be used as a routing condition. Incoming connections will query the internal registry
	 *  to invoke the matching handlers.
	 * 
	 * e.g. "/test/{param}". Params are resolved using {@link HttpChannel#param(String)} 
	 * 
	 * @param path The {@link HttpSelector} to resolve against this path, pattern matching and capture are supported
	 * @param handler an handler to invoke for the given condition
	 * 
	 * @return {@code this}
	 */
	public final HttpServer get(String path,
	                                     final ReactorChannelHandler> handler) {
		route(NetSelectors.get(path), handler);
		return this;
	}

	/**
	 * Listen for HTTP POST on the passed path to be used as a routing condition. Incoming connections will query the internal registry
	 *  to invoke the matching handlers.
	 *
	 * e.g. "/test/{param}". Params are resolved using {@link HttpChannel#param(String)}
	 *
	 * @param path The {@link HttpSelector} to resolve against this path, pattern matching and capture are supported
	 * @param handler an handler to invoke for the given condition
	 * @return {@code this}
	 */
	public final HttpServer post(String path,
	                                      final ReactorChannelHandler> handler) {
		route(NetSelectors.post(path), handler);
		return this;
	}


	/**
	 * Listen for HTTP PUT on the passed path to be used as a routing condition. Incoming connections will query the internal registry
	 *  to invoke the matching handlers.
	 *
	 * e.g. "/test/{param}". Params are resolved using {@link HttpChannel#param(String)}
	 *
	 * @param path The {@link HttpSelector} to resolve against this path, pattern matching and capture are supported
	 * @param handler an handler to invoke for the given condition
	 * 
	 * @return {@code this}
	 */
	public final HttpServer put(String path,
	                                     final ReactorChannelHandler> handler) {
		route(NetSelectors.put(path), handler);
		return this;
	}


	/**
	 * Listen for WebSocket on the passed path to be used as a routing condition. Incoming connections will query the internal registry
	 *  to invoke the matching handlers.
	 *
	 * e.g. "/test/{param}". Params are resolved using {@link HttpChannel#param(String)}
	 *
	 * @param path The {@link HttpSelector} to resolve against this path, pattern matching and capture are supported
	 * @param handler an handler to invoke for the given condition
	 *
	 * @return {@code this}
	 */
	public final HttpServer ws(String path,
	                                     final ReactorChannelHandler> handler) {
		route(NetSelectors.get(path), handler);
		hasWebsocketEndpoints = true;
		return this;
	}

	/**
	 * Listen for HTTP DELETE on the passed path to be used as a routing condition. Incoming connections will query the internal registry
	 *  to invoke the matching handlers.
	 *
	 * e.g. "/test/{param}". Params are resolved using {@link HttpChannel#param(String)}
	 *
	 * @param path The {@link HttpSelector} to resolve against this path, pattern matching and capture are supported
	 * @param handler an handler to invoke for the given condition
	 *
	 * @return {@code this}
	 */
	public final HttpServer delete(String path,
	                                        final ReactorChannelHandler> handler) {
		route(NetSelectors.delete(path), handler);
		return this;
	}

	protected abstract void onWebsocket(HttpChannel next);

	protected final boolean hasWebsocketEndpoints(){
		return hasWebsocketEndpoints;
	}

	protected Iterable> routeChannel(final HttpChannel ch) {
		final List>>>
				selected = routedWriters.select(ch);

		if(hasWebsocketEndpoints) {
			String connection = ch.headers().get(HttpHeaders.CONNECTION);
			if (connection != null && connection.equals(HttpHeaders.UPGRADE)) {
				onWebsocket(ch);
			}
		}

		return new Iterable>() {
			@Override
			public Iterator> iterator() {
				final Iterator>>>
						iterator = selected.iterator();

				return new Iterator>() {
					@Override
					public boolean hasNext() {
						return iterator.hasNext();
					}

					@Override
					public void remove() {
						iterator.remove();
					}

					//Lazy apply
					@Override
					@SuppressWarnings("unchecked")
					public Publisher next() {
						Registration>> next = iterator.next();
						if (next != null) {
							ch.paramsResolver(next.getSelector().getHeaderResolver());
							return next.getObject().apply(ch);
						} else {
							return null;
						}
					}
				};
			}
		};
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy