/*
* Copyright (C) 2015 SoftIndex LLC.
*
* 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 io.datakernel.http;
import io.datakernel.async.ForwardingResultCallback;
import io.datakernel.async.IgnoreCompletionCallback;
import io.datakernel.async.ResultCallback;
import io.datakernel.bytebuf.ByteBufPool;
import io.datakernel.dns.AsyncDnsClient;
import io.datakernel.eventloop.Eventloop;
import io.datakernel.net.DatagramSocketSettings;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import static io.datakernel.bytebuf.ByteBufPool.getPoolItemsString;
import static io.datakernel.bytebuf.ByteBufStrings.decodeAscii;
import static io.datakernel.bytebuf.ByteBufStrings.encodeAscii;
import static io.datakernel.eventloop.FatalErrorHandlers.rethrowOnAnyError;
import static io.datakernel.http.TestUtils.readFully;
import static io.datakernel.http.TestUtils.toByteArray;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class SimpleProxyServerTest {
final static int ECHO_SERVER_PORT = 9707;
final static int PROXY_SERVER_PORT = 9444;
@Before
public void before() {
ByteBufPool.clear();
ByteBufPool.setSizes(0, Integer.MAX_VALUE);
}
public static AsyncHttpServer proxyHttpServer(final Eventloop primaryEventloop, final AsyncHttpClient httpClient) {
AsyncServlet servlet = new AsyncServlet() {
@Override
public void serve(HttpRequest request, final ResultCallback<HttpResponse> callback) {
httpClient.send(HttpRequest.get("http://127.0.0.1:" + ECHO_SERVER_PORT + request.getUrl().getPath()), new ForwardingResultCallback<HttpResponse>(callback) {
@Override
public void onResult(final HttpResponse result) {
int code = result.getCode();
byte[] body = encodeAscii("FORWARDED: " + decodeAscii(result.getBody()));
callback.setResult(HttpResponse.ofCode(code).withBody(body));
}
});
}
};
return AsyncHttpServer.create(primaryEventloop, servlet).withListenPort(PROXY_SERVER_PORT);
}
public static AsyncHttpServer echoServer(Eventloop primaryEventloop) {
AsyncServlet servlet = new AsyncServlet() {
@Override
public void serve(HttpRequest request, ResultCallback<HttpResponse> callback) {
HttpResponse content = HttpResponse.ok200().withBody(encodeAscii(request.getUrl().getPathAndQuery()));
callback.setResult(content);
}
};
return AsyncHttpServer.create(primaryEventloop, servlet).withListenPort(ECHO_SERVER_PORT);
}
private void readAndAssert(InputStream is, String expected) throws IOException {
byte[] bytes = new byte[expected.length()];
readFully(is, bytes);
Assert.assertEquals(expected, decodeAscii(bytes));
}
@Test
public void testSimpleProxyServer() throws Exception {
Eventloop eventloop1 = Eventloop.create().withFatalErrorHandler(rethrowOnAnyError());
AsyncHttpServer echoServer = echoServer(eventloop1);
echoServer.listen();
Thread echoServerThread = new Thread(eventloop1);
echoServerThread.start();
Eventloop eventloop2 = Eventloop.create().withFatalErrorHandler(rethrowOnAnyError());
AsyncDnsClient dnsClient = AsyncDnsClient.create(eventloop2)
.withDatagramSocketSetting(DatagramSocketSettings.create())
.withDnsServerAddress(HttpUtils.inetAddress("8.8.8.8"));
final AsyncHttpClient httpClient = AsyncHttpClient.create(eventloop2).withDnsClient(dnsClient);
AsyncHttpServer proxyServer = proxyHttpServer(eventloop2, httpClient);
proxyServer.listen();
Thread proxyServerThread = new Thread(eventloop2);
proxyServerThread.start();
Socket socket = new Socket();
socket.connect(new InetSocketAddress(PROXY_SERVER_PORT));
OutputStream stream = socket.getOutputStream();
stream.write(encodeAscii("GET /abc HTTP1.1\r\nHost: localhost\r\nConnection: keep-alive\n\r\n"));
readAndAssert(socket.getInputStream(), "HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nContent-Length: 15\r\n\r\nFORWARDED: /abc");
stream.write(encodeAscii("GET /hello HTTP1.1\r\nHost: localhost\r\nConnection: close\n\r\n"));
readAndAssert(socket.getInputStream(), "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 17\r\n\r\nFORWARDED: /hello");
httpClient.getEventloop().execute(new Runnable() {
@Override
public void run() {
httpClient.stop(IgnoreCompletionCallback.create());
}
});
echoServer.closeFuture().get();
proxyServer.closeFuture().get();
assertTrue(toByteArray(socket.getInputStream()).length == 0);
socket.close();
echoServerThread.join();
proxyServerThread.join();
assertEquals(getPoolItemsString(), ByteBufPool.getCreatedItems(), ByteBufPool.getPoolItems());
}
}