├── .gitignore ├── _config.yml ├── .travis.yml ├── src └── main │ ├── resources │ ├── conf │ │ └── application.properties │ ├── META-INF │ │ └── spring.handlers │ ├── spring │ │ ├── beans-proxy-routes.xml │ │ ├── beans-vertx.xml │ │ └── samples │ │ │ └── beans-proxy-routes-samples.xml │ └── log4j2.xml │ └── java │ └── com │ └── github │ └── kislayverma │ └── throo │ ├── routes │ ├── BaseRouteConfigurer.java │ ├── ProxyRouteConfigurer.java │ ├── ProxyRouteFactory.java │ └── ProxyRoute.java │ ├── bootstrap │ ├── ApplicationConfig.java │ ├── SpringContextLoader.java │ └── Application.java │ ├── util │ └── MiscUtil.java │ └── verticle │ └── HttpProxyVerticle.java ├── README.md └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | target/* 2 | logs/* 3 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | install: mvn clean package -Dgpg.skip=true -U 3 | jdk: 4 | - oraclejdk8 -------------------------------------------------------------------------------- /src/main/resources/conf/application.properties: -------------------------------------------------------------------------------- 1 | spring.configs=spring/beans-vertx.xml,spring/beans-proxy-routes.xml 2 | application.verticleInstances=1 3 | application.workerPoolSize=25 4 | application.workerPoolName="proxyWorkerPool" -------------------------------------------------------------------------------- /src/main/java/com/github/kislayverma/throo/routes/BaseRouteConfigurer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kislay Verma. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.kislayverma.throo.routes; 17 | 18 | import io.vertx.core.Vertx; 19 | import io.vertx.ext.web.Router; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | /** 24 | * 25 | * @author Kislay Verma 26 | */ 27 | public abstract class BaseRouteConfigurer { 28 | 29 | protected Vertx vertx; 30 | 31 | protected final Logger LOGGER = LoggerFactory.getLogger(this.getClass()); 32 | 33 | public abstract Router setRouteHandlers(Vertx vertx); 34 | } 35 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring.handlers: -------------------------------------------------------------------------------- 1 | http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler 2 | http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler 3 | http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler 4 | http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler 5 | http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler 6 | http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler 7 | http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler 8 | http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler 9 | http\://www.springframework.org/schema/jdbc=org.springframework.jdbc.config.JdbcNamespaceHandler 10 | http\://www.springframework.org/schema/security=org.springframework.security.config.SecurityNamespaceHandler 11 | http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler 12 | http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler 13 | -------------------------------------------------------------------------------- /src/main/resources/spring/beans-proxy-routes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/kislayverma/throo.svg?branch=master)](https://travis-ci.org/kislayverma/throo) 2 | 3 | # Throo 4 | A Vert.x/Spring based HTTP reverse-proxy 5 | 6 | ## How to use this application 7 | ### Clone 8 | Clone this repository to your system. 9 | 10 | ### Configure proxy routes 11 | Open the "src/main/resources/spring/beans-proxy-routes.xml" file with your favourite editor and add the routes you want to proxy there along with other data to get this proxy working. A few sample routes are given under "src/main/resources/spring/samples/". 12 | 13 | ### Build 14 | mvn clean package 15 | This will generate a "throo-[version]-fat.jar" under the "target" folder. This is an executable fat jar. 16 | 17 | ### Run 18 | Execute the fat jar using the command : java -jar target/throo--fat.jar 19 | This should start up Nexus on port 8080 of your machine. Hitting http://localhost:8080/[your proxied route] should get you the data from the target server. 20 | 21 | If you face problems related to DNS resolution (i.e. if you get an error saying the targte host could not be resolved, or no such host etc.), try running the application by disabling the netty DNS resolver and defaulting to the JDK DNS resolver. 22 | 23 | Like this : java -Dvertx.disableDnsResolver=true -jar target/throo-[version]-fat.jar 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/kislayverma/throo/bootstrap/ApplicationConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kislay Verma. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.kislayverma.throo.bootstrap; 17 | 18 | import java.io.FileNotFoundException; 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.util.Properties; 22 | 23 | /** 24 | * 25 | * @author Kislay Verma 26 | */ 27 | public class ApplicationConfig { 28 | 29 | private final Properties prop; 30 | 31 | public ApplicationConfig(String propFileName) throws IOException { 32 | InputStream inputStream = getClass().getClassLoader().getResourceAsStream(propFileName); 33 | 34 | if (inputStream != null) { 35 | prop = new Properties(); 36 | prop.load(inputStream); 37 | } else { 38 | throw new FileNotFoundException("Property file '" + propFileName + "' not found in the classpath"); 39 | } 40 | } 41 | 42 | public String getProperty(String propertyName) { 43 | return prop.getProperty(propertyName); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/resources/spring/beans-vertx.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/main/java/com/github/kislayverma/throo/util/MiscUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kislay Verma. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.kislayverma.throo.util; 17 | 18 | import com.github.kislayverma.throo.routes.ProxyRoute; 19 | import io.vertx.ext.web.RoutingContext; 20 | import io.vertx.ext.web.client.HttpRequest; 21 | import io.vertx.ext.web.client.HttpResponse; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | /** 26 | * 27 | * @author Kislay Verma 28 | */ 29 | public class MiscUtil { 30 | private static final Logger LOGGER = LoggerFactory.getLogger(MiscUtil.class); 31 | 32 | public static HttpRequest setRequestHeaders(HttpRequest request, RoutingContext routingContext, ProxyRoute proxyRoute) { 33 | routingContext.request().headers().entries().forEach((entry) -> { 34 | LOGGER.debug("Setting request header : " + entry.getKey() + " : " + entry.getValue()); 35 | request.putHeader(entry.getKey(), entry.getValue()); 36 | }); 37 | request.putHeader("Host", proxyRoute.getTargetBaseUrl()); 38 | 39 | return request; 40 | } 41 | 42 | public static void setResponseHeaders(HttpResponse response, RoutingContext routingContext) { 43 | response.headers().entries().forEach((entry) -> { 44 | LOGGER.debug("Setting response header : " + entry.getKey() + " : " + entry.getValue()); 45 | routingContext.response().putHeader(entry.getKey(), entry.getValue()); 46 | }); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/main/java/com/github/kislayverma/throo/bootstrap/SpringContextLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kislay Verma. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.kislayverma.throo.bootstrap; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | import org.springframework.context.ApplicationContext; 21 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 22 | import org.springframework.context.support.ClassPathXmlApplicationContext; 23 | 24 | /** 25 | * 26 | * @author Kislay Verma 27 | */ 28 | public class SpringContextLoader { 29 | private final AnnotationConfigApplicationContext ctx; 30 | private final ApplicationContext context; 31 | private static final Logger LOGGER = LoggerFactory.getLogger(SpringContextLoader.class); 32 | 33 | public SpringContextLoader(String[] springContextFilePaths) { 34 | LOGGER.info("Loading Spring configs..."); 35 | ctx = new AnnotationConfigApplicationContext(); 36 | ctx.refresh(); 37 | ctx.registerShutdownHook(); 38 | 39 | context = new ClassPathXmlApplicationContext(springContextFilePaths); 40 | for (String beanName : context.getBeanDefinitionNames()) { 41 | LOGGER.debug("Loading spring bean : " + beanName); 42 | ctx.getBeanFactory().registerSingleton(beanName, context.getBean(beanName)); 43 | } 44 | 45 | LOGGER.info("Finished loading spring beans"); 46 | } 47 | 48 | public A getBean(Class clazz) { 49 | return ctx.getBean(clazz); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/github/kislayverma/throo/routes/ProxyRouteConfigurer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kislay Verma. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.kislayverma.throo.routes; 17 | 18 | import io.vertx.core.Vertx; 19 | import io.vertx.core.http.HttpMethod; 20 | import io.vertx.ext.web.Router; 21 | import io.vertx.ext.web.handler.BodyHandler; 22 | import io.vertx.ext.web.handler.ResponseTimeHandler; 23 | 24 | /** 25 | * This class gets and loads all the proxy routes. 26 | * @author Kislay Verma 27 | */ 28 | public class ProxyRouteConfigurer extends BaseRouteConfigurer { 29 | 30 | private static ProxyRouteFactory proxyRouteFactory; 31 | 32 | @Override 33 | public Router setRouteHandlers(Vertx vertx) { 34 | LOGGER.info("Configuring proxy routes and registering handlers..."); 35 | 36 | Router router = Router.router(vertx); 37 | 38 | // Enable response time handlers fr all requests and body handler for all POST and PUT requests 39 | router.route().handler(ResponseTimeHandler.create()); 40 | router.route(HttpMethod.POST, "/*").handler(BodyHandler.create()); 41 | router.route(HttpMethod.PUT, "/*").handler(BodyHandler.create()); 42 | 43 | // Load all the proxy routes 44 | for (ProxyRoute proxyRoute : proxyRouteFactory.getAllRoutes()) { 45 | LOGGER.info("Configuring route : [" + proxyRoute.getHttpMethod() + "] " + proxyRoute.getRoute()); 46 | router = proxyRoute.configure(vertx, router); 47 | } 48 | 49 | return router; 50 | } 51 | 52 | public void setProxyRouteFactory(ProxyRouteFactory aProxyRouteFactory) { 53 | proxyRouteFactory = aProxyRouteFactory; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/github/kislayverma/throo/verticle/HttpProxyVerticle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kislay Verma. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.kislayverma.throo.verticle; 17 | 18 | import com.github.kislayverma.throo.routes.BaseRouteConfigurer; 19 | import io.vertx.core.AbstractVerticle; 20 | import io.vertx.core.Future; 21 | import io.vertx.core.http.HttpServerOptions; 22 | import io.vertx.ext.web.Router; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | /** 27 | * 28 | * @author Kislay Verma 29 | */ 30 | public class HttpProxyVerticle extends AbstractVerticle { 31 | 32 | private static BaseRouteConfigurer routeConfigurer; 33 | private static final Logger LOGGER = LoggerFactory.getLogger(HttpProxyVerticle.class); 34 | 35 | @Override 36 | public void start(Future fut) { 37 | LOGGER.info("Starting proxy verticle..."); 38 | 39 | // Set up all the routes and their handlers 40 | Router router = routeConfigurer.setRouteHandlers(vertx); 41 | HttpServerOptions options = new HttpServerOptions().setLogActivity(true); 42 | // Create the HTTP server 43 | vertx.createHttpServer(options) 44 | .requestHandler(router::accept) 45 | .listen( 46 | // Retrieve the port from the configuration, 47 | // default to 8080. 48 | config().getInteger("http.port", 8080), 49 | result -> { 50 | if (result.succeeded()) { 51 | fut.complete(); 52 | } else { 53 | fut.fail(result.cause()); 54 | } 55 | } 56 | ); 57 | } 58 | 59 | public void setRouteConfigurer(BaseRouteConfigurer aRouteConfigurer) { 60 | routeConfigurer = aRouteConfigurer; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/github/kislayverma/throo/routes/ProxyRouteFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kislay Verma. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.kislayverma.throo.routes; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collections; 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | import org.springframework.beans.BeansException; 26 | import org.springframework.context.ApplicationContext; 27 | import org.springframework.context.ApplicationContextAware; 28 | 29 | /** 30 | * 31 | * @author Kislay Verma 32 | */ 33 | public class ProxyRouteFactory implements ApplicationContextAware { 34 | 35 | private ApplicationContext context; 36 | 37 | private Map keyToBeanMap; 38 | 39 | private static final Logger LOGGER = LoggerFactory.getLogger(ProxyRouteFactory.class); 40 | public void init() { 41 | Map h = new HashMap<>(); 42 | 43 | for (ProxyRoute bean : getBeans()) { 44 | try { 45 | LOGGER.info("Adding bean with key :" + bean.getRoute() + "-- class name :" + bean.getClass()); 46 | h.put(bean.getRoute(), bean); 47 | } catch (Exception e) { 48 | LOGGER.debug("Error initializing ProxyRouteFactory", e); 49 | } 50 | } 51 | synchronized (this.getClass()) { 52 | keyToBeanMap = Collections.unmodifiableMap(h); 53 | } 54 | } 55 | 56 | public ProxyRoute getBean(String key) { 57 | ProxyRoute bean = keyToBeanMap.get(key); 58 | if (bean == null) { 59 | LOGGER.error("No bean with key " + key); 60 | } 61 | 62 | return bean; 63 | } 64 | 65 | public List getAllRoutes() { 66 | return this.getBeans(); 67 | } 68 | 69 | private List getBeans() { 70 | return new ArrayList<>(context.getBeansOfType(ProxyRoute.class, true, true).values()); 71 | } 72 | 73 | @Override 74 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 75 | this.context = applicationContext; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/resources/spring/samples/beans-proxy-routes-samples.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/main/java/com/github/kislayverma/throo/bootstrap/Application.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kislay Verma. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.kislayverma.throo.bootstrap; 17 | 18 | import com.github.kislayverma.throo.verticle.HttpProxyVerticle; 19 | import io.vertx.core.DeploymentOptions; 20 | import io.vertx.core.Vertx; 21 | import java.io.IOException; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | /** 26 | * This is the class that launches the application. 27 | * Lifted from https://github.com/yasha-tymoshenko/vertx-rest-crud/blob/master/src/main/java/com/tymoshenko/MainApp.java 28 | * 29 | * @author Kislay Verma 30 | */ 31 | public class Application { 32 | private static final Logger LOGGER = LoggerFactory.getLogger(Application.class); 33 | 34 | public static void main(String[] args) throws IOException { 35 | ApplicationConfig config = new ApplicationConfig("conf/application.properties"); 36 | String springFilesStr = config.getProperty("spring.configs"); 37 | if (springFilesStr != null) { 38 | String[] springFilesArr = springFilesStr.split(","); 39 | if (springFilesArr.length > 0) { 40 | LOGGER.info("Loading spring config files : " + springFilesStr); 41 | SpringContextLoader contextLoader = new SpringContextLoader(springFilesArr); 42 | } 43 | } 44 | 45 | int verticleInstances = 1; 46 | String configuredVerticleInstances = config.getProperty("application.verticleInstances"); 47 | if (configuredVerticleInstances != null && !configuredVerticleInstances.isEmpty()) { 48 | verticleInstances = Integer.parseInt(configuredVerticleInstances); 49 | } 50 | LOGGER.info("Number of proxy verticles to instantiate : " + verticleInstances); 51 | 52 | int workerPoolSize = 25; 53 | String configuredWorkerPoolSize = config.getProperty("application.workerPoolSize"); 54 | if (configuredWorkerPoolSize != null && !configuredWorkerPoolSize.isEmpty()) { 55 | workerPoolSize = Integer.parseInt(configuredWorkerPoolSize); 56 | } 57 | LOGGER.info("Worker pool size : " + workerPoolSize); 58 | 59 | String workerPoolName = "proxyWorkerPool"; 60 | String configuredWorkerPoolName = config.getProperty("application.workerPoolName"); 61 | if (configuredWorkerPoolName != null && !configuredWorkerPoolName.isEmpty()) { 62 | workerPoolName = configuredWorkerPoolName; 63 | } 64 | LOGGER.info("Worker pool name : " + workerPoolName); 65 | 66 | final Vertx vertx = Vertx.vertx(); 67 | 68 | LOGGER.info("Deploying verticles..."); 69 | vertx.deployVerticle(HttpProxyVerticle.class.getName(),new DeploymentOptions() 70 | .setInstances(verticleInstances).setWorkerPoolSize(workerPoolSize).setWorkerPoolName(workerPoolName)); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/github/kislayverma/throo/routes/ProxyRoute.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kislay Verma. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.kislayverma.throo.routes; 17 | 18 | import com.github.kislayverma.throo.util.MiscUtil; 19 | import io.vertx.core.AsyncResult; 20 | import io.vertx.core.Vertx; 21 | import io.vertx.core.buffer.Buffer; 22 | import io.vertx.ext.web.Router; 23 | import io.vertx.ext.web.RoutingContext; 24 | import io.vertx.ext.web.client.HttpRequest; 25 | import io.vertx.ext.web.client.HttpResponse; 26 | import io.vertx.ext.web.client.WebClient; 27 | import io.vertx.ext.web.client.WebClientOptions; 28 | import java.util.Date; 29 | import org.slf4j.Logger; 30 | import org.slf4j.LoggerFactory; 31 | 32 | /** 33 | * 34 | * @author Kislay Verma 35 | */ 36 | public class ProxyRoute { 37 | private String route; 38 | private String targetBaseUrl; 39 | private String httpMethod; 40 | private Long timeout; 41 | private Integer port; 42 | private WebClient client; 43 | 44 | private final Logger LOGGER = LoggerFactory.getLogger(this.getClass()); 45 | private static final String START_TIME_KEY = "proxyHandlerStartTime"; 46 | 47 | public Router configure(Vertx vertx, Router router) { 48 | WebClientOptions clientOptions = new WebClientOptions().setLogActivity(true).setMaxPoolSize(100); 49 | this.client = WebClient.create(vertx, clientOptions); 50 | 51 | switch (httpMethod.toUpperCase()) { 52 | case "GET": 53 | router.get(route).handler(this::doProxyGet); 54 | break; 55 | case "POST": 56 | router.post(route).handler(this::doProxyPost); 57 | break; 58 | case "PUT": 59 | router.put(route).handler(this::doProxyPut); 60 | break; 61 | default: 62 | throw new RuntimeException("Unsupported HTTP method on proxy route " + route); 63 | } 64 | 65 | return router; 66 | } 67 | 68 | public void doProxyGet(RoutingContext routingContext) { 69 | LOGGER.debug("Matched route " + route); 70 | 71 | long timeoutToUse = this.timeout == null ? 1000L : this.timeout; 72 | int portToUse = (this.port == null) ? 80 : this.port; 73 | 74 | routingContext.put(START_TIME_KEY, new Date().getTime()); 75 | // Blind passthrough - only change the host and port part of the incoming request. 76 | // Sending the request URI will send both the path and the query params onwards 77 | HttpRequest request = client.get(portToUse, targetBaseUrl, routingContext.request().uri()).timeout(timeoutToUse); 78 | request = MiscUtil.setRequestHeaders(request, routingContext, this); 79 | 80 | request.send(r -> { 81 | AsyncResult> ar = (AsyncResult) r; 82 | handleResponse(routingContext, ar); 83 | }); 84 | } 85 | 86 | private void doProxyPost(RoutingContext routingContext) { 87 | LOGGER.debug("Matched route " + route); 88 | 89 | long timeoutToUse = this.timeout == null ? 1000L : this.timeout; 90 | int portToUse = (this.port == null) ? 80 : this.port; 91 | 92 | routingContext.put(START_TIME_KEY, new Date().getTime()); 93 | // Blind passthrough - only change the host and port part of the incoming request. 94 | // Sending the request URI will send both the path and the query params onwards 95 | HttpRequest request = client.post(portToUse, targetBaseUrl, routingContext.request().uri()).timeout(timeoutToUse); 96 | request = MiscUtil.setRequestHeaders(request, routingContext, this); 97 | LOGGER.debug("Request body : " + routingContext.getBody()); 98 | request.sendBuffer(routingContext.getBody(), r -> { 99 | AsyncResult> ar = (AsyncResult) r; 100 | handleResponse(routingContext, ar); 101 | }); 102 | } 103 | 104 | private void doProxyPut(RoutingContext routingContext) { 105 | LOGGER.debug("Matched route " + route); 106 | 107 | long timeoutToUse = this.timeout == null ? 1000L : this.timeout; 108 | int portToUse = (this.port == null) ? 80 : this.port; 109 | 110 | routingContext.put(START_TIME_KEY, new Date().getTime()); 111 | // Blind passthrough - only change the host and port part of the incoming request. 112 | // Sending the request URI will send both the path and the query params onwards 113 | HttpRequest request = client.put(portToUse, targetBaseUrl, routingContext.request().uri()).timeout(timeoutToUse); 114 | request = MiscUtil.setRequestHeaders(request, routingContext, this); 115 | LOGGER.debug("Request body : " + routingContext.getBody()); 116 | request.sendBuffer(routingContext.getBody(), r -> { 117 | AsyncResult> ar = (AsyncResult) r; 118 | handleResponse(routingContext, ar); 119 | }); 120 | } 121 | 122 | private void handleResponse(RoutingContext routingContext, AsyncResult> ar) { 123 | if (ar.succeeded()) { 124 | HttpResponse response = ar.result(); 125 | long timeTaken = (new Date().getTime()) - ((Long)routingContext.get(START_TIME_KEY)); 126 | LOGGER.info("Request : " + (targetBaseUrl + routingContext.request().uri()) + 127 | "\tResponse status code : " + response.statusCode() + 128 | "\tResponse time : " + timeTaken); 129 | LOGGER.debug("Response body : " + response.bodyAsString()); 130 | 131 | MiscUtil.setResponseHeaders(response, routingContext); 132 | routingContext.response().setStatusCode(response.statusCode()); 133 | routingContext.response().setStatusMessage(response.statusMessage()); 134 | 135 | routingContext.response().end(response.bodyAsBuffer()); 136 | } else { 137 | routingContext.fail(ar.cause()); 138 | } 139 | } 140 | 141 | public String getRoute() { 142 | return route; 143 | } 144 | 145 | public void setRoute(String route) { 146 | this.route = route; 147 | } 148 | 149 | public String getTargetBaseUrl() { 150 | return targetBaseUrl; 151 | } 152 | 153 | public void setTargetBaseUrl(String targetBaseUrl) { 154 | this.targetBaseUrl = targetBaseUrl; 155 | } 156 | 157 | public String getHttpMethod() { 158 | return httpMethod; 159 | } 160 | 161 | public void setHttpMethod(String httpMethod) { 162 | this.httpMethod = httpMethod; 163 | } 164 | 165 | public Long getTimeout() { 166 | return timeout; 167 | } 168 | 169 | public void setTimeout(Long timeout) { 170 | this.timeout = timeout; 171 | } 172 | 173 | public Integer getPort() { 174 | return port; 175 | } 176 | 177 | public void setPort(Integer port) { 178 | this.port = port; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.github.kislayverma 4 | throo 5 | 1.0.0-SNAPSHOT 6 | jar 7 | Throo 8 | A Vert.x/Spring based HTTP-HTTP reverse proxy 9 | https://github.com/kislayverma/nexus 10 | 11 | 12 | 13 | The Apache License, Version 2.0 14 | http://www.apache.org/licenses/LICENSE-2.0.txt 15 | 16 | 17 | 18 | 19 | 20 | Kislay Verma 21 | kislay.nsit@gmail.com 22 | None 23 | https://github.com/kislayverma 24 | 25 | 26 | 27 | 28 | scm:git:git@github.com:kislayverma/throo.git 29 | scm:git:git@github.com:kislayverma/throo.git 30 | scm:git:git@github.com:kislayverma/throo.git 31 | HEAD 32 | 33 | 34 | 35 | 36 | ossrh 37 | https://oss.sonatype.org/content/repositories/snapshots 38 | 39 | 40 | ossrh 41 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 42 | 43 | 44 | 45 | 46 | io.vertx 47 | vertx-core 48 | 3.4.2 49 | 50 | 51 | io.vertx 52 | vertx-web 53 | 3.4.2 54 | 55 | 56 | io.vertx 57 | vertx-web-client 58 | 3.4.2 59 | 60 | 61 | org.springframework 62 | spring-context 63 | 4.3.9.RELEASE 64 | 65 | 66 | org.springframework 67 | spring-core 68 | 4.3.9.RELEASE 69 | 70 | 71 | org.springframework 72 | spring-aop 73 | 4.3.9.RELEASE 74 | 75 | 76 | org.springframework 77 | spring-context-support 78 | 4.3.9.RELEASE 79 | 80 | 81 | org.apache.logging.log4j 82 | log4j-core 83 | 2.8.2 84 | 85 | 86 | org.apache.logging.log4j 87 | log4j-slf4j-impl 88 | 2.8.2 89 | 90 | 91 | org.apache.logging.log4j 92 | log4j-1.2-api 93 | 2.8.2 94 | 95 | 96 | org.slf4j 97 | slf4j-api 98 | 1.7.25 99 | 100 | 101 | junit 102 | junit 103 | 4.8.1 104 | jar 105 | test 106 | 107 | 108 | 109 | 110 | 111 | 112 | maven-compiler-plugin 113 | 3.6.1 114 | 115 | 1.8 116 | 1.8 117 | 118 | 119 | 120 | org.apache.maven.plugins 121 | maven-shade-plugin 122 | 3.0.0 123 | 124 | 125 | package 126 | 127 | shade 128 | 129 | 130 | 131 | 132 | 134 | 135 | com.github.kislayverma.throo.bootstrap.Application 136 | com.github.kislayverma.throo.verticle.HttpProxyVerticle 137 | 138 | 139 | 144 | 145 | META-INF/spring.schemas 146 | 147 | 148 | 149 | ${project.build.directory}/${project.artifactId}-${project.version}-fat.jar 150 | 151 | 152 | 153 | 154 | 155 | org.codehaus.mojo 156 | cobertura-maven-plugin 157 | 2.7 158 | 159 | 160 | 0 161 | 162 | 163 | 164 | 165 | org.apache.maven.plugins 166 | maven-surefire-plugin 167 | 2.18.1 168 | true 169 | 170 | 171 | org.apache.maven.plugins 172 | maven-pmd-plugin 173 | 3.5 174 | 175 | 176 | 177 | pmd 178 | 179 | 180 | 181 | 182 | true 183 | utf-8 184 | 100 185 | 1.7 186 | 187 | 188 | 189 | org.apache.maven.plugins 190 | maven-source-plugin 191 | 2.4 192 | 193 | 194 | attach-sources 195 | 196 | jar 197 | 198 | 199 | 200 | 201 | 202 | org.apache.maven.plugins 203 | maven-javadoc-plugin 204 | 2.10.3 205 | 206 | 207 | attach-javadocs 208 | 209 | jar 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | org.apache.maven.plugins 221 | maven-jxr-plugin 222 | 2.5 223 | 224 | 225 | org.apache.maven.plugins 226 | maven-project-info-reports-plugin 227 | 2.9 228 | 229 | false 230 | 231 | 232 | 233 | org.apache.maven.plugins 234 | maven-javadoc-plugin 235 | 2.10.3 236 | 237 | 238 | org.codehaus.mojo 239 | cobertura-maven-plugin 240 | 2.7 241 | 242 | 243 | org.apache.maven.plugins 244 | maven-pmd-plugin 245 | 3.6 246 | 247 | true 248 | utf-8 249 | 100 250 | 1.7 251 | 252 | 253 | 254 | 255 | 256 | --------------------------------------------------------------------------------