Skip to content

Commit 61a15e8

Browse files
committed
added code for v0.1.0
1 parent 9005eb9 commit 61a15e8

File tree

4 files changed

+377
-0
lines changed

4 files changed

+377
-0
lines changed

pom.xml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2+
<modelVersion>4.0.0</modelVersion>
3+
<groupId>net.b07z.sepia.proxies</groupId>
4+
<artifactId>sepia-reverse-proxy</artifactId>
5+
<version>0.1.0</version>
6+
<packaging>jar</packaging>
7+
8+
<properties>
9+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
10+
<maven.compiler.source>1.8</maven.compiler.source>
11+
<maven.compiler.target>1.8</maven.compiler.target>
12+
</properties>
13+
14+
<build>
15+
<plugins>
16+
<plugin>
17+
<groupId>org.apache.maven.plugins</groupId>
18+
<artifactId>maven-dependency-plugin</artifactId>
19+
<version>3.1.0</version>
20+
<executions>
21+
<execution>
22+
<id>copy-dependencies</id>
23+
<phase>prepare-package</phase>
24+
<goals>
25+
<goal>copy-dependencies</goal>
26+
</goals>
27+
<configuration>
28+
<outputDirectory>
29+
${project.build.directory}/libs
30+
</outputDirectory>
31+
</configuration>
32+
</execution>
33+
</executions>
34+
</plugin>
35+
<plugin>
36+
<groupId>org.apache.maven.plugins</groupId>
37+
<artifactId>maven-jar-plugin</artifactId>
38+
<version>3.1.0</version>
39+
<configuration>
40+
<archive>
41+
<manifest>
42+
<addClasspath>true</addClasspath>
43+
<classpathPrefix>libs/</classpathPrefix>
44+
<mainClass>
45+
net.b07z.sepia.proxies.Start
46+
</mainClass>
47+
</manifest>
48+
</archive>
49+
<finalName>sepia-reverse-proxy-v${project.version}</finalName>
50+
</configuration>
51+
</plugin>
52+
</plugins>
53+
</build>
54+
55+
<dependencies>
56+
<dependency>
57+
<groupId>io.undertow</groupId>
58+
<artifactId>undertow-core</artifactId>
59+
<version>2.0.1.Final</version>
60+
</dependency>
61+
</dependencies>
62+
</project>
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package net.b07z.sepia.proxies;
2+
3+
/**
4+
* Command-line interface to start a proxy.
5+
*
6+
* @author Florian Quirin
7+
*
8+
*/
9+
public class Start {
10+
11+
/**
12+
* Run a proxy.
13+
* @param args
14+
* @throws Exception
15+
*/
16+
public static void main(String[] args) throws Exception {
17+
String proxy = "";
18+
19+
//Proxy to run:
20+
if (args[0].equals("tiny")){
21+
proxy = "tiny";
22+
23+
//default
24+
int port = 20726;
25+
String host = "localhost";
26+
27+
for (String arg : args){
28+
//Port
29+
if (arg.startsWith("-port=")){
30+
port = Integer.parseInt(arg.replaceFirst(".*?=", "").trim());
31+
32+
//Host
33+
}else if (arg.startsWith("-host=")){
34+
host = arg.replaceFirst(".*?=", "").trim();
35+
36+
//Paths
37+
}else if (arg.startsWith("-defaultPaths=")){
38+
String paths = arg.replaceFirst(".*?=", "").trim();
39+
if (!paths.equals("true")){
40+
System.out.println("Sorry any other than the default paths are not yet supported via command-line interface!");
41+
return;
42+
}
43+
//TODO: add a way to define custom prefix-path combinations (best: load from config and give config-file here as value)
44+
}
45+
}
46+
47+
//Create tiny reverse proxy
48+
TinyReverseProxy reverseProxy = new TinyReverseProxy(host, port);
49+
50+
//Add paths - SEPIA defaults for custom-bundle:
51+
reverseProxy.addPrefixPath("/sepia/assist", "http://localhost:20721");
52+
reverseProxy.addPrefixPath("/sepia/teach", "http://localhost:20722");
53+
reverseProxy.addPrefixPath("/sepia/chat", "http://localhost:20723");
54+
55+
//Start proxy
56+
reverseProxy.start();
57+
58+
//Note
59+
System.out.println("SEPIA '" + proxy + "' reverse proxy started at: " + host + ":" + port);
60+
61+
return;
62+
63+
//Help
64+
}else{
65+
help();
66+
return;
67+
}
68+
}
69+
70+
/**
71+
* Command-line interface help.
72+
*/
73+
private static void help(){
74+
System.out.println("\nUsage:");
75+
System.out.println("[proxy-name] [arguments]");
76+
System.out.println("\nProxies:");
77+
System.out.println("tiny - args: -defaultPaths=true, -port=20726, -host=localhost");
78+
System.out.println("");
79+
}
80+
81+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package net.b07z.sepia.proxies;
2+
3+
import java.net.URI;
4+
import java.util.HashMap;
5+
import java.util.Map;
6+
7+
import io.undertow.Handlers;
8+
import io.undertow.Undertow;
9+
import io.undertow.Undertow.Builder;
10+
import io.undertow.server.handlers.PathHandler;
11+
import io.undertow.server.handlers.proxy.LoadBalancingProxyClient;
12+
import io.undertow.server.handlers.proxy.ProxyHandler;
13+
import io.undertow.util.Headers;
14+
15+
/**
16+
* Very basic, tiny, reverse proxy based on Undertow.
17+
*
18+
* @author Florian Quirin
19+
*
20+
*/
21+
public class TinyReverseProxy {
22+
23+
int IO_THREADS = 3;
24+
int LB_CONNECTIONS_PER_THREAD = 3;
25+
int MAX_REQ_TIME = 30000;
26+
27+
String host = "localhost";
28+
int port = 20726;
29+
30+
Undertow reverseProxy;
31+
int state = 0; //0: pre-built, 1: started, 2: stopped
32+
33+
Map<String, LoadBalancingProxyClient> prefixMappings;
34+
Map<String, LoadBalancingProxyClient> exactMappings;
35+
36+
/**
37+
* Create server running at port.
38+
* @param host - usually "localhost"
39+
* @param port - port to run on
40+
*/
41+
public TinyReverseProxy(String host, int port){
42+
this.host = host;
43+
this.port = port;
44+
prefixMappings = new HashMap<>();
45+
exactMappings = new HashMap<>();
46+
}
47+
48+
/**
49+
* Start server. Add some proxy-paths first!
50+
*/
51+
public void start(){
52+
Builder proxyBuilder = Undertow.builder()
53+
.addHttpListener(this.port, this.host)
54+
.setIoThreads(IO_THREADS);
55+
56+
PathHandler pathHandler = Handlers.path();
57+
pathHandler.addExactPath("/", (exchange) -> {
58+
exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
59+
exchange.getResponseSender().send("SEPIA reverse-proxy powered by Undertow");
60+
});
61+
62+
//Exact-paths
63+
for (String path : exactMappings.keySet()){
64+
// REST API path
65+
pathHandler.addExactPath(path,
66+
ProxyHandler.builder()
67+
.setProxyClient(prefixMappings.get(path))
68+
.setMaxRequestTime(MAX_REQ_TIME).build()
69+
);
70+
}
71+
//Prefix-paths
72+
for (String path : prefixMappings.keySet()){
73+
// REST API path
74+
pathHandler.addPrefixPath(path,
75+
ProxyHandler.builder()
76+
.setProxyClient(prefixMappings.get(path))
77+
.setMaxRequestTime(MAX_REQ_TIME).build()
78+
);
79+
}
80+
proxyBuilder.setHandler(pathHandler);
81+
82+
reverseProxy = proxyBuilder.build();
83+
reverseProxy.start();
84+
state = 1;
85+
}
86+
87+
/**
88+
* Stop server.
89+
*/
90+
public void stop(){
91+
reverseProxy.stop();
92+
state = 2;
93+
}
94+
95+
/**
96+
* Add a reverse proxy path and target.
97+
* @param path - path of server to forward, e.g. "/sepia"
98+
* @param target - target server, e.g. "http://localhost:20721"
99+
* @throws Exception
100+
*/
101+
public void addPrefixPath(String path, String target) throws Exception{
102+
if (state != 0){
103+
throw new RuntimeException("Not possible while reverse-proxy is running!");
104+
}else{
105+
LoadBalancingProxyClient loadBalancer = new LoadBalancingProxyClient()
106+
.addHost(new URI(target))
107+
.setConnectionsPerThread(LB_CONNECTIONS_PER_THREAD);
108+
prefixMappings.put(path, loadBalancer);
109+
}
110+
}
111+
112+
/**
113+
* Remove previously set path.
114+
* @param path - path of server to forward, e.g. "/sepia"
115+
*/
116+
public void removePrefixPath(String path){
117+
if (state != 0){
118+
throw new RuntimeException("Not possible while reverse-proxy is running!");
119+
}else{
120+
prefixMappings.remove(path);
121+
}
122+
}
123+
124+
//TODO: add and test exact path mappings
125+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package net.b07z.sepia.proxies;
2+
3+
import static org.junit.Assert.*;
4+
5+
import java.io.BufferedReader;
6+
import java.io.IOException;
7+
import java.io.InputStreamReader;
8+
import java.net.HttpURLConnection;
9+
import java.net.URL;
10+
11+
import org.junit.Test;
12+
13+
import io.undertow.Undertow;
14+
import io.undertow.server.HttpServerExchange;
15+
import io.undertow.util.Headers;
16+
17+
public class TestSimpleReverseProxy {
18+
19+
@Test
20+
public void test() throws Exception {
21+
22+
//Start test-server
23+
Undertow s1 = startTestServer(9213, "Test1");
24+
Undertow s2 = startTestServer(9214, "Test2");
25+
26+
//Create tiny reverse proxy
27+
TinyReverseProxy reverseProxy = new TinyReverseProxy("localhost", 9212);
28+
29+
//Add paths
30+
reverseProxy.addPrefixPath("/one", "http://localhost:9213");
31+
reverseProxy.addPrefixPath("/two", "http://localhost:9214");
32+
reverseProxy.addPrefixPath("/sepia/three", "http://localhost:9213");
33+
reverseProxy.addPrefixPath("/sepia/four", "http://localhost:9214");
34+
35+
//Start proxy
36+
reverseProxy.start();
37+
38+
//Test-calls and close server
39+
String res = httpGET("http://localhost:9213"); assertTrue(res.equals("Test1 - path: /"));
40+
res = httpGET("http://localhost:9214"); assertTrue(res.equals("Test2 - path: /"));
41+
res = httpGET("http://localhost:9212"); assertTrue(res.startsWith("SEPIA"));
42+
res = httpGET("http://localhost:9212/one/1"); assertTrue(res.equals("Test1 - path: /1"));
43+
res = httpGET("http://localhost:9212/two/2"); assertTrue(res.equals("Test2 - path: /2"));
44+
res = httpGET("http://localhost:9212/sepia/three/3"); assertTrue(res.equals("Test1 - path: /3"));
45+
res = httpGET("http://localhost:9212/sepia/four/4"); assertTrue(res.equals("Test2 - path: /4"));
46+
47+
reverseProxy.stop();
48+
s1.stop();
49+
s2.stop();
50+
51+
assertTrue(true);
52+
}
53+
54+
/**
55+
* Start a test server at port with custom message response in plain text.
56+
*/
57+
private Undertow startTestServer(int port, String msg){
58+
Undertow server = Undertow.builder()
59+
.addHttpListener(port, "localhost")
60+
.setHandler((exchange) -> {
61+
exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
62+
exchange.getResponseSender().send(makeTestMessage(exchange, msg));
63+
}).build();
64+
server.start();
65+
return server;
66+
}
67+
/**
68+
* Make a test message for test server
69+
*/
70+
private String makeTestMessage(HttpServerExchange ex, String msg){
71+
String fullMsg = msg + " - path: " + ex.getRequestPath();
72+
//System.out.println(fullMsg);
73+
return fullMsg;
74+
}
75+
76+
/**
77+
* Make a HTTP GET call.
78+
*/
79+
private static String httpGET(String url) throws IOException {
80+
URL obj = new URL(url);
81+
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
82+
con.setRequestMethod("GET");
83+
con.setRequestProperty("User-Agent", "Mozilla/5.0");
84+
con.setConnectTimeout(3000);
85+
con.setReadTimeout(3000);
86+
87+
int responseCode = con.getResponseCode();
88+
//System.out.println("GET Response Code : " + responseCode); //debug
89+
90+
//success?
91+
if (responseCode >= 200 && responseCode < 300){
92+
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
93+
String inputLine;
94+
StringBuffer response = new StringBuffer();
95+
while ((inputLine = in.readLine()) != null) {
96+
response.append(inputLine);
97+
}
98+
in.close();
99+
//result
100+
String result = response.toString();
101+
//System.out.println(response.toString()); //debug
102+
return result;
103+
104+
}else{
105+
return String.valueOf(responseCode);
106+
}
107+
}
108+
109+
}

0 commit comments

Comments
 (0)