Separate "file transfer proxy"? - control and transfer services

for the purpose of separating services, I patched class FileTransferProxy.

Your comments and suggestions are welcome.

  1. suguesstions:

1.1 quick and dirty patch class FileTransferProxy to support external file transfer service.

1.2 consider adding new server properties, such as externalHost, proxyService.

1.3 think about how to support more than one file server, by:

  1. enhancing file proxy configuration,

  2. and/or enhancing relative JEPs

  1. test case for the “quick and dirty patch”:

2.1 environment:

two linux boxes, one windows box, each has just one net interface.

  1. BOX_A - 192.168.0.10, as IM server && file transfer proxy’'s “control service”

  2. BOX_B - 192.168.0.238, as file file transfer proxy’'s “transfer service”

  3. BOX_C - 192.168.0.227, with Exodus and Pis as jabber clients.

2.2 configurations:

  1. configure BOX_B’'s host name as “ft”,

make sure all servers and the client can resolve ft as 192.168.0.238 -

by adding “192.168.0.10 ft” into system config file “host” on both linux and windows box.

  1. eanble both hosts’’ file transfer proxy, and configure “xmpp.proxy.port” as 7777.

  2. set BOX_A’‘s property “xmpp.proxy.externalip” as “ft”, i.e. BOX_B’'s name.

  3. leave BOX_B’'s property “xmpp.proxy.externalip” as default value - “127.0.0.1”.

  4. configure Exodus’'s sending file method as “Discovering a file transfer component using my server.”

2.3 more details and result:

  1. create two jabber users(USER_A, USER_B) in BOX_A’'s wildfire server, subscribe each other.

  2. login USER_A to one Exodus window, and login USER_B to another Exodus window or Psi window

  3. send file from USER_A to USER_B, it’'s ok.

  1. patch for class FileTransferProxy - revision 3481:

— FileTransferProxy.java 2006-03-11 00:00:03.000000000 +0800

+++ FileTransferProxy_new.java 2006-03-11 00:21:00.000000000 +0800

@@ -22,6 +22,7 @@

import org.jivesoftware.wildfire.disco.DiscoServerItem;

import org.jivesoftware.wildfire.disco.ServerItemsProvider;

import org.jivesoftware.wildfire.forms.spi.XDataFormImpl;

+import org.jivesoftware.wildfire.server.OutgoingServerSession;

import org.xmpp.packet.IQ;

import org.xmpp.packet.JID;

import org.xmpp.packet.Packet;

@@ -46,6 +47,8 @@

RoutableChannelHandler {

 private static final String NAMESPACE = "http://jabber.org/protocol/bytestreams";
  • private static final String loopbackIP = “127.0.0.1”;

private String proxyServiceName;

@@ -54,12 +57,16 @@

private PacketRouter router;

private String proxyIP;

private ProxyConnectionManager connectionManager;

  • private SessionManager sm;

  • private String serverName;

public FileTransferProxy() {

super(“SOCKS5 file transfer proxy”);

info = new IQHandlerInfo(“query”, NAMESPACE);

  •    sm = SessionManager.getInstance();
    
  •    serverName = XMPPServer.getInstance().getServerInfo().getName();
    

}

public boolean handleIQ(IQ packet) throws UnauthorizedException {

@@ -107,22 +114,65 @@

return true;

}

else if (packet.getType() == IQ.Type.set && childElement != null) {

  •            String sid = childElement.attributeValue("sid");
    
  •            JID from = packet.getFrom();
    
  •            JID to = new JID(childElement.elementTextTrim("activate"));
    
  •             //NOTE: proxyIP should be proxyHost, or add a server property named "xmpp.proxy.proxyHost";
    
  •             String proxyHost = proxyIP;
    
  •             Log.error("serverName=" + serverName + ", proxyHost=" + proxyHost);
    
  •             String local_ip = null, proxy_ip = null;
    
  •             try {
    
  •                  local_ip = InetAddress.getByName(serverName).getHostAddress();
    
  •             } catch (Exception e) {}
    
  •             try {
    
  •                  proxy_ip = InetAddress.getByName(proxyHost).getHostAddress();
    
  •             } catch (Exception e) {}
    
  •             /*
    
  •              * should route to external file server? quick and dirty solution:
    
  •              *
    
  •              * proxy is not local loopback interface &&
    
  •              * local host name(or IP) are not equals proxy''s host name(or IP) &&
    
  •              * both IPs are not equals
    
  •              */
    
  •             if(! proxyHost.equals(loopbackIP) && ! serverName.equals(proxyHost) && (local_ip != null && ! local_ip.equals(proxy_ip))) {
    
  •                  /*
    
  •                   * rename packet''s attribute ''to'' according to proxyHost
    
  •                   * NOTE: magic string "proxy" should be configured along with proxyHost: (proxyHost, proxyService)
    
  •                   */
    
  •                  String to = "proxy." + proxyHost;
    
  •            IQ reply = IQ.createResultIQ(packet);
    
  •            try {
    
  •                connectionManager.activate(from, to, sid);
    
  •            }
    
  •            catch (IllegalArgumentException ie) {
    
  •                Log.error("Error activating connection", ie);
    
  •                reply.setType(IQ.Type.error);
    
  •                reply.setError(new PacketError(PacketError.Condition.not_allowed));
    
  •            }
    
  •                  OutgoingServerSession oss = sm.getOutgoingServerSession(proxyHost);
    
  •                  packet.setTo(new JID(to));
    
  •                  IQ reply = IQ.createResultIQ(packet);
    
  •                  if(oss == null) {
    
  •                       Log.error("can''t find the outgoing server session for " + proxyHost);
    
  •                       reply.setType(IQ.Type.error);
    
  •                     reply.setError(new PacketError(PacketError.Condition.not_allowed));
    
  •                  } else {
    
  •                       oss.process(packet);
    
  •                  }
    
  •                router.route(reply);
    
  •             } else {
    
  •            router.route(reply);
    
  •            return true;
    
  •                 String sid = childElement.attributeValue("sid");
    
  •                 JID from = packet.getFrom();
    
  •                 JID to = new JID(childElement.elementTextTrim("activate"));
    
  •                 IQ reply = IQ.createResultIQ(packet);
    
  •                 try {
    
  •                     connectionManager.activate(from, to, sid);
    
  •                 }
    
  •                 catch (IllegalArgumentException ie) {
    
  •                     Log.error("Error activating connection", ie);
    
  •                     reply.setType(IQ.Type.error);
    
  •                     reply.setError(new PacketError(PacketError.Condition.not_allowed));
    
  •                 }
    
  •                 router.route(reply);
    
  •             }
    
  •             return true;
    

}

}

return false;

Message was edited by: woloo

Message was edited by: woloo

This is intresting, I just have a few question. Why seperate them?

As we all know, file transfer take up socket resource, need CPU cycles, consume network bandwidth.

AFAIK, Wildfire can only be configured as a standalone server, now. The coming “N front-end connection managers + one core router” solution is to achieve high scalability as described in the Pampero project. In both solutions, only one (core) router exist.

Comparing to the original implementation, separating file transfer proxy into control service and transfer service, can decrease the core router’'s actual throughput.

Best regards, woloo