package com.limegroup.gnutella.lws.server;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import org.limewire.lws.server.LWSSenderOfMessagesToServer;
import org.limewire.lws.server.StringCallback;
import org.limewire.net.SocketsManager;
import org.limewire.service.ErrorService;
/**
* This class defines instances of {@link LWSSenderOfMessagesToServer} that
* simply communicate to a another server running on a given host and port. For
* the purpose of testing it's used to talk to another server running on
* <code>localhost</code>.
*/
public final class LocalServerDelegate {
private final int port;
private final SocketsManager socketsManager;
private final String host;
/**
* @param host host name to which we communicate
* @param port port to which we communicate
* @param openner openner that will create a socket from <code>host</code>
* and <code>port</code>
*/
public LocalServerDelegate(SocketsManager openner, String host, int port) {
this.socketsManager = openner;
this.host = host;
this.port = port;
}
/**
* This defines an interface to construct a URL based on a message and
* arguments. This is used becaused when we're sending a URL to a server on
* the client we encode the arguments as normal CGI parameters; when sending
* to a mock remote server, we use a Wicket-style encoding. See
* {@link #constructURL(String msg, Map<String, String> args)} for examples.
*/
public interface URLConstructor {
/**
* Returns a URL based on the base message, <code>msg</code> and
* arguments. Examples, for a message <code>"Foo"</code> and arguments
* <code>{ "a" => "A", "b" => "B" }</code> would be:
*
* <p>
*
* <table>
* <tr>
* <th>Type</th>
* <th>URL</th>
* </tr>
* <tr>
* <td>Normal</td>
* <td><code>Foo?a=A&b=B</code></td>
* </tr>
* <tr>
* <td>Wicket</td>
* <td><code>store/app/pages/client/ClientCom/command/Foo/a/A/b/B</code></td>
* </tr>
* </table>
*
* @param msg base message
* @param args arguments
* @return a URL based on the base message, <code>msg</code> and
* arguments
*/
String constructURL(String msg, Map<String, String> args);
}
/**
* An implementation of {@link URLConstructor} using Wicket-style URLs. An
* example, for a message <code>"Foo"</code> and arguments
* <code>{ "a" => "A", "b" => "B" }</code> would be:
* <code>store/app/pages/client/ClientCom/command/Foo/a/A/b/B</code>.
*/
public static class WicketStyleURLConstructor implements URLConstructor {
public final static WicketStyleURLConstructor INSTANCE = new WicketStyleURLConstructor();
private WicketStyleURLConstructor() {}
public String constructURL(String msg, Map<String, String> args) {
StringBuffer sb = new StringBuffer("store/app/pages/client/ClientCom/command");
sb.append("/").append(msg);
for (String key : args.keySet()) {
sb.append("/")
.append(key)
.append("/")
.append(args.get(key));
}
return sb.toString();
}
}
/**
* An implementation of {@link URLConstructor} using normal CGI
* parameter-encoding. An example, for a message <code>"Foo"</code> and
* arguments <code>{ "a" => "A", "b" => "B" }</code> would be
* <code>Foo?a=A&b=B</code>.
*/
public static class NormalStyleURLConstructor implements URLConstructor {
public final static NormalStyleURLConstructor INSTANCE = new NormalStyleURLConstructor();
private NormalStyleURLConstructor() {}
public String constructURL(String msg, Map<String, String> args) {
StringBuffer sb = new StringBuffer(msg);
boolean first = true;
for (String key : args.keySet()) {
sb.append(first ? "?" : "&")
.append(key)
.append("=")
.append(args.get(key));
first = false;
}
return sb.toString();
}
}
public void sendMessageToServer(String msg, Map<String, String> args, StringCallback cb, URLConstructor ctor) {
try {
int tmpPort = port;
Socket tmpSock = null;
for (; tmpPort < port + 10; tmpPort++) {
try { tmpSock = socketsManager.connect(new InetSocketAddress(host,port), 5000);} catch (IOException e) { /* skip */ }
if (tmpSock != null) break;
}
final Socket sock = tmpSock;
BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
//
// We'll always be adding this from the web page
// to ensure a new script is loaded
//
Map<String,String> newArgs = new HashMap<String,String>(args);
newArgs.put("_f", String.valueOf(System.currentTimeMillis()));
String request = ctor.constructURL(msg, newArgs);
wr.write("GET /" + request + " HTTP/1.1\n\r\n\r");
wr.flush();
BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
StringBuffer sb = new StringBuffer();
//
// We will have a similar response and can only give
// StringCallbacks the actual response
//
// HTTP/1.1 200 OK
// Last-modified: Fri, 19-10-2007 14:38:54 GMT
// Server: null
// Date: Fri Oct 19 14:38:54 EDT 2007
// Content-length: 2
//
// ok
//
// So just extract the 'ok'
//
String ln;
boolean reading = false;
//
// Read past the header
//
while ((ln = in.readLine()) != null) {
if (reading) {
sb.append(ln);
sb.append("\n");
} else if (ln.equals("") || ln.equals("\n")) {
reading = true;
}
}
String res = sb.toString();
in.close();
wr.close();
sock.close();
cb.process(res);
} catch (IOException e) {
ErrorService.error(e, "trying to connect on " + host + ":" + port);
}
}
}