/*
* TrackerTest.java
*
* Created on Feb 2, 2010, 10:26:58 AM
*
* Description: .
*
* Copyright (C) Feb 2, 2010 reed.
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.texai.torrent;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import org.apache.commons.codec.net.URLCodec;
import org.apache.log4j.Logger;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.texai.network.netty.handler.AbstractBitTorrentHandlerFactory;
import org.texai.network.netty.handler.AbstractHTTPRequestHandlerFactory;
import org.texai.network.netty.handler.AbstractHTTPResponseHandler;
import org.texai.network.netty.handler.HTTPRequestHandler;
import org.texai.network.netty.handler.HTTPRequestHandlerFactory;
import org.texai.network.netty.pipeline.HTTPClientPipelineFactory;
import org.texai.network.netty.pipeline.PortUnificationChannelPipelineFactory;
import org.texai.util.NetworkUtils;
import org.texai.x509.KeyStoreTestUtils;
import org.texai.x509.X509SecurityInfo;
import static org.junit.Assert.*;
/**
*
* @author reed
*/
public final class TrackerTest {
/** the logger */
private static final Logger LOGGER = Logger.getLogger(TrackerTest.class);
/** the server port */
private static final int SERVER_PORT = 8088;
/** the info hash */
private static final byte[] INFO_HASH = {
0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08,
0x09, 0x0A, 0x0B, 0x0C,
0x0D, 0x0E, 0x0F, 0x10,
0x11, 0x12, 0x13, 0x14};
/** sets debugging */
// static {
// System.setProperty("javax.net.debug", "all");
// }
public TrackerTest() {
}
@BeforeClass
public static void setUpClass() throws Exception {
}
@AfterClass
public static void tearDownClass() throws Exception {
}
@Before
public void setUp() {
}
@After
public void tearDown() {
}
/**
* Test of bit torrent tracker.
*/
@Test
public void testTracker() {
LOGGER.info("tracker");
// configure the HTTP request handler by registering the tracker
final HTTPRequestHandler httpRequestHandler = HTTPRequestHandler.getInstance();
final Tracker tracker = new Tracker();
tracker.addInfoHash(new String((new URLCodec()).encode(INFO_HASH)));
httpRequestHandler.register(tracker);
// configure the server channel pipeline factory
final AbstractBitTorrentHandlerFactory bitTorrentHandlerFactory = new MockBitTorrentHandlerFactory();
final AbstractHTTPRequestHandlerFactory httpRequestHandlerFactory = new HTTPRequestHandlerFactory();
final X509SecurityInfo x509SecurityInfo = KeyStoreTestUtils.getServerX509SecurityInfo();
final ChannelPipelineFactory channelPipelineFactory = new PortUnificationChannelPipelineFactory(
null, // albusHCNMessageHandlerFactory,
bitTorrentHandlerFactory,
httpRequestHandlerFactory,
x509SecurityInfo);
// configure the server
final ServerBootstrap serverBootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
assertEquals("{}", serverBootstrap.getOptions().toString());
serverBootstrap.setPipelineFactory(channelPipelineFactory);
// bind and start to accept incoming connections
serverBootstrap.bind(new InetSocketAddress("localhost", SERVER_PORT));
// test tracker client
httpClient();
final Timer timer = new Timer();
timer.schedule(new ShutdownTimerTask(), 5000);
// shut down executor threads to exit
LOGGER.info("releasing server resources");
serverBootstrap.releaseExternalResources();
timer.cancel();
}
/** Provides a task to run when the external resources cannot be released. */
private static final class ShutdownTimerTask extends TimerTask {
/** Runs the timer task. */
@Override
public void run() {
LOGGER.info("server resources not released");
System.exit(0);
}
}
/** Tests the HTTP request and response messages. */
@SuppressWarnings({"ThrowableResultIgnored", "null"})
private void httpClient() {
final ClientBootstrap clientBootstrap = new ClientBootstrap(new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
// configure the client pipeline
final Object clientResume_lock = new Object();
final AbstractHTTPResponseHandler httpResponseHandler = new MockHTTPResponseHandler(clientResume_lock);
final X509SecurityInfo x509SecurityInfo = KeyStoreTestUtils.getClientX509SecurityInfo();
final ChannelPipeline channelPipeline = HTTPClientPipelineFactory.getPipeline(
httpResponseHandler,
x509SecurityInfo);
clientBootstrap.setPipeline(channelPipeline);
LOGGER.info("pipeline: " + channelPipeline.toString());
// start the connection attempt
ChannelFuture channelFuture = clientBootstrap.connect(new InetSocketAddress("localhost", SERVER_PORT));
// wait until the connection attempt succeeds or fails
final Channel channel = channelFuture.awaitUninterruptibly().getChannel();
if (!channelFuture.isSuccess()) {
channelFuture.getCause().printStackTrace();
fail(channelFuture.getCause().getMessage());
}
LOGGER.info("HTTP client connected");
URI uri = null;
HttpRequest httpRequest;
String host;
// send the statistics request
try {
uri = new URI("https://localhost:" + SERVER_PORT + "/torrent-tracker/statistics");
} catch (URISyntaxException ex) {
fail(ex.getMessage());
}
httpRequest = new DefaultHttpRequest(
HttpVersion.HTTP_1_1,
HttpMethod.GET,
uri.toASCIIString());
host = uri.getHost() == null ? "localhost" : uri.getHost();
httpRequest.setHeader(HttpHeaders.Names.HOST, host);
httpRequest.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
LOGGER.info("httpRequest ...\n" + httpRequest);
channel.write(httpRequest);
// wait for the request message to be sent
channelFuture.awaitUninterruptibly();
if (!channelFuture.isSuccess()) {
channelFuture.getCause().printStackTrace();
fail(channelFuture.getCause().getMessage());
}
// send the scrape request
try {
uri = new URI("https://localhost:" + SERVER_PORT + "/torrent-tracker/scrape");
} catch (URISyntaxException ex) {
fail(ex.getMessage());
}
httpRequest = new DefaultHttpRequest(
HttpVersion.HTTP_1_1,
HttpMethod.GET,
uri.toASCIIString());
host = uri.getHost() == null ? "localhost" : uri.getHost();
httpRequest.setHeader(HttpHeaders.Names.HOST, host);
httpRequest.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
LOGGER.info("httpRequest ...\n" + httpRequest);
channel.write(httpRequest);
// wait for the request message to be sent
channelFuture.awaitUninterruptibly();
if (!channelFuture.isSuccess()) {
channelFuture.getCause().printStackTrace();
fail(channelFuture.getCause().getMessage());
}
// send the announce request
final byte[] myPeerIdBytes = {
0x14, 0x13, 0x12, 0x11,
0x10, 0x0F, 0x0E, 0x0D,
0x0C, 0x0B, 0x0A, 0x09,
0x08, 0x07, 0x06, 0x05,
0x04, 0x03, 0x02, 0x01};
final int nbrBytesUploaded = 0;
final int nbrBytesDownloaded = 0;
final int nbrBytesLeftToDownloaded = 1024;
final String event = "started";
final String myIPAddress = NetworkUtils.getLocalHostAddress().getHostAddress();
final StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("https://localhost:");
stringBuilder.append(SERVER_PORT);
stringBuilder.append("/torrent-tracker/announce");
stringBuilder.append('?');
stringBuilder.append("info_hash=");
stringBuilder.append(new String((new URLCodec()).encode(INFO_HASH)));
stringBuilder.append("&peer_id=");
stringBuilder.append(new String((new URLCodec()).encode(myPeerIdBytes)));
stringBuilder.append("&port=");
stringBuilder.append(SERVER_PORT);
stringBuilder.append("&uploaded=");
stringBuilder.append(nbrBytesUploaded);
stringBuilder.append("&downloaded=");
stringBuilder.append(nbrBytesDownloaded);
stringBuilder.append("&left=");
stringBuilder.append(nbrBytesLeftToDownloaded);
stringBuilder.append("&event=");
stringBuilder.append(event);
stringBuilder.append("&ip=");
stringBuilder.append(myIPAddress);
try {
uri = new URI(stringBuilder.toString());
} catch (URISyntaxException ex) {
fail(ex.getMessage());
}
httpRequest = new DefaultHttpRequest(
HttpVersion.HTTP_1_1,
HttpMethod.GET,
uri.toASCIIString());
host = uri.getHost() == null ? "localhost" : uri.getHost();
httpRequest.setHeader(HttpHeaders.Names.HOST, host);
httpRequest.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
LOGGER.info("httpRequest ...\n" + httpRequest);
channel.write(httpRequest);
// wait for the request message to be sent
channelFuture.awaitUninterruptibly();
if (!channelFuture.isSuccess()) {
channelFuture.getCause().printStackTrace();
fail(channelFuture.getCause().getMessage());
}
// the message response handler will signal this thread when the test exchanges are completed
synchronized (clientResume_lock) {
try {
clientResume_lock.wait();
} catch (InterruptedException ex) {
}
}
LOGGER.info("releasing HTTP client resources");
channel.close();
clientBootstrap.releaseExternalResources();
}
}