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

io.scalecube.services.gateway.http.HttpGateway Maven / Gradle / Ivy

The newest version!
package io.scalecube.services.gateway.http;

import static io.netty.handler.codec.http.HttpMethod.DELETE;
import static io.netty.handler.codec.http.HttpMethod.GET;
import static io.netty.handler.codec.http.HttpMethod.HEAD;
import static io.netty.handler.codec.http.HttpMethod.OPTIONS;
import static io.netty.handler.codec.http.HttpMethod.PATCH;
import static io.netty.handler.codec.http.HttpMethod.POST;
import static io.netty.handler.codec.http.HttpMethod.PUT;
import static io.netty.handler.codec.http.HttpMethod.TRACE;

import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.cors.CorsConfigBuilder;
import io.netty.handler.codec.http.cors.CorsHandler;
import io.scalecube.services.Address;
import io.scalecube.services.ServiceCall;
import io.scalecube.services.exceptions.DefaultErrorMapper;
import io.scalecube.services.exceptions.ServiceProviderErrorMapper;
import io.scalecube.services.gateway.Gateway;
import io.scalecube.services.registry.api.ServiceRegistry;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import reactor.netty.DisposableServer;
import reactor.netty.http.server.HttpServer;
import reactor.netty.resources.LoopResources;

public class HttpGateway implements Gateway {

  public static final List SUPPORTED_METHODS =
      List.of(GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE);

  private final String id;
  private final int port;
  private final Function callFactory;
  private final ServiceProviderErrorMapper errorMapper;
  private final boolean corsEnabled;
  private final CorsConfigBuilder corsConfigBuilder;

  private DisposableServer server;
  private LoopResources loopResources;

  private HttpGateway(Builder builder) {
    this.id = builder.id;
    this.port = builder.port;
    this.callFactory = builder.callFactory;
    this.errorMapper = builder.errorMapper;
    this.corsEnabled = builder.corsEnabled;
    this.corsConfigBuilder = builder.corsConfigBuilder;
  }

  public static Builder builder() {
    return new Builder();
  }

  @Override
  public String id() {
    return id;
  }

  @Override
  public Gateway start(ServiceCall call, ServiceRegistry serviceRegistry) {
    loopResources =
        LoopResources.create(id + ":" + port, LoopResources.DEFAULT_IO_WORKER_COUNT, true);

    try {
      server =
          HttpServer.create()
              .runOn(loopResources)
              .bindAddress(() -> new InetSocketAddress(port))
              .doOnConnection(
                  connection -> {
                    if (corsEnabled) {
                      connection.addHandlerLast(new CorsHandler(corsConfigBuilder.build()));
                    }
                  })
              .handle(
                  new HttpGatewayAcceptor(callFactory.apply(call), serviceRegistry, errorMapper))
              .bind()
              .toFuture()
              .get();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    return this;
  }

  @Override
  public Address address() {
    InetSocketAddress address = (InetSocketAddress) server.address();
    return Address.create(address.getHostString(), address.getPort());
  }

  @Override
  public void stop() {
    shutdownServer(server);
    shutdownLoopResources(loopResources);
  }

  private void shutdownServer(DisposableServer server) {
    if (server != null) {
      server.dispose();
    }
  }

  private void shutdownLoopResources(LoopResources loopResources) {
    if (loopResources != null) {
      loopResources.dispose();
    }
  }

  public static class Builder {

    private String id = "http@" + Integer.toHexString(hashCode());
    private int port;
    private Function callFactory = call -> call;
    private ServiceProviderErrorMapper errorMapper = DefaultErrorMapper.INSTANCE;
    private boolean corsEnabled = false;
    private CorsConfigBuilder corsConfigBuilder =
        CorsConfigBuilder.forAnyOrigin()
            .allowedRequestMethods(SUPPORTED_METHODS.toArray(HttpMethod[]::new))
            .allowNullOrigin()
            .maxAge(3600);

    private Builder() {}

    public String id() {
      return id;
    }

    public Builder id(String id) {
      this.id = id;
      return this;
    }

    public int port() {
      return port;
    }

    public Builder port(int port) {
      this.port = port;
      return this;
    }

    public Builder serviceCall(Function operator) {
      callFactory = callFactory.andThen(operator);
      return this;
    }

    public ServiceProviderErrorMapper errorMapper() {
      return errorMapper;
    }

    public Builder errorMapper(ServiceProviderErrorMapper errorMapper) {
      this.errorMapper = errorMapper;
      return this;
    }

    public boolean corsEnabled() {
      return corsEnabled;
    }

    public Builder corsEnabled(boolean corsEnabled) {
      this.corsEnabled = corsEnabled;
      return this;
    }

    public CorsConfigBuilder corsConfigBuilder() {
      return corsConfigBuilder;
    }

    public Builder corsConfigBuilder(CorsConfigBuilder corsConfigBuilder) {
      this.corsConfigBuilder = corsConfigBuilder;
      return this;
    }

    public Builder corsConfigBuilder(Consumer consumer) {
      consumer.accept(corsConfigBuilder);
      return this;
    }

    public HttpGateway build() {
      return new HttpGateway(this);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy