diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java
index 95221c47710134a18bdc99668fdd9f61be6df33f..d56d0787cb50b540456f1e7d91158cc1cc20d696 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java
@@ -90,7 +90,7 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R
          "Your browser is misconfigured. Do not use the proxy to access the router console or other localhost destinations.<BR>")
         .getBytes();
     
-    private final static byte[] SUCCESS_RESPONSE =
+    final static byte[] SUCCESS_RESPONSE =
         ("HTTP/1.1 200 Connection Established\r\n"+
          "Proxy-agent: I2P\r\n"+
          "\r\n")
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java
index 05a41860a6e0243290b1ee4d8a9610acec6dfda0..65af72159431522a9bd6d632154116f5a7495378 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java
@@ -18,6 +18,9 @@ import java.util.concurrent.ConcurrentHashMap;
 
 import net.i2p.I2PAppContext;
 import net.i2p.I2PException;
+import net.i2p.app.ClientApp;
+import net.i2p.app.ClientAppManager;
+import net.i2p.app.Outproxy;
 import net.i2p.client.I2PSession;
 import net.i2p.client.streaming.I2PSocket;
 import net.i2p.client.streaming.I2PSocketManager;
@@ -29,7 +32,6 @@ import net.i2p.data.DataHelper;
 import net.i2p.data.Destination;
 import net.i2p.data.Hash;
 import net.i2p.i2ptunnel.localServer.LocalHTTPServer;
-import net.i2p.outproxy.Outproxy;
 import net.i2p.util.EventDispatcher;
 import net.i2p.util.Log;
 import net.i2p.util.PortMapper;
@@ -416,6 +418,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
                      ****/
                     }
 
+                    method = params[0];
+                    if (method.toUpperCase(Locale.US).equals("CONNECT")) {
+                        // this makes things easier later, by spoofing a
+                        // protocol so the URI parser find the host and port
+                        // FIXME breaks in-net outproxy
+                        request = "https://" + request + '/';
+                    }
+
                     // Now use the Java URI parser
                     // This will be the incoming URI but will then get modified
                     // to be the outgoing URI (with http:// if going to outproxy, otherwise without)
@@ -447,13 +457,13 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
                         s.close();
                         return;
                     }
-                    method = params[0];
+
                     String protocolVersion = params[2];
 
                     protocol = requestURI.getScheme();
                     host = requestURI.getHost();
                     if(protocol == null || host == null) {
-                        _log.warn("Null protocol or host: " + request);
+                        _log.warn("Null protocol or host: " + request + ' ' + protocol + ' ' + host);
                         method = null;
                         break;
                     }
@@ -531,6 +541,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
                                 break;
                             }
                           ******/
+                        } else if ("https".equals(protocol) ||
+                                   method.toUpperCase(Locale.US).equals("CONNECT")) {
+                            remotePort = 443;
                         } else {
                             remotePort = 80;
                         }
@@ -681,18 +694,28 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
                         s.close();
                         return;
                     } else if(host.contains(".") || host.startsWith("[")) {
-                        if (Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_OUTPROXY)) &&
-                            (outproxy = _context.outproxy()) != null) {
-                            int rPort = requestURI.getPort();
-                            if (rPort > 0)
-                                remotePort = rPort;
-                            else
-                                remotePort = 80;
-                            usingOutproxy = true;
-                            targetRequest = requestURI.toASCIIString();
-                            if(_log.shouldLog(Log.DEBUG))
-                                _log.debug(getPrefix(requestId) + " [" + host + "]: outproxy!");
-                        } else {
+                        if (Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_OUTPROXY))) {
+                            ClientAppManager mgr = _context.clientAppManager();
+                            if (mgr != null) {
+                                ClientApp op = mgr.getRegisteredApp(Outproxy.NAME);
+                                if (op != null) {
+                                    outproxy = (Outproxy) op;
+                                    int rPort = requestURI.getPort();
+                                    if (rPort > 0)
+                                        remotePort = rPort;
+                                    else if ("https".equals(protocol) ||
+                                             method.toUpperCase(Locale.US).equals("CONNECT"))
+                                        remotePort = 443;
+                                    else
+                                        remotePort = 80;
+                                    usingOutproxy = true;
+                                    targetRequest = requestURI.toASCIIString();
+                                    if(_log.shouldLog(Log.DEBUG))
+                                        _log.debug(getPrefix(requestId) + " [" + host + "]: outproxy!");
+                                }
+                            }
+                        }
+                        if (!usingOutproxy) {
                             if(port >= 0) {
                                 host = host + ':' + port;
                             }
@@ -917,9 +940,17 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
             // no destination, going to outproxy plugin
             if (usingOutproxy) {
                 Socket outSocket = outproxy.connect(host, remotePort);
-                byte[] data = newRequest.toString().getBytes("ISO-8859-1");
                 Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
-                new I2PTunnelOutproxyRunner(s, outSocket, sockLock, data, onTimeout);
+                byte[] data;
+                byte[] response;
+                if (method.toUpperCase(Locale.US).equals("CONNECT")) {
+                    data = null;
+                    response = I2PTunnelConnectClient.SUCCESS_RESPONSE;
+                } else {
+                    data = newRequest.toString().getBytes("ISO-8859-1");
+                    response = null;
+                }
+                new I2PTunnelOutproxyRunner(s, outSocket, sockLock, data, response, onTimeout);
                 return;
             }
 
@@ -1025,9 +1056,22 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
             if (remotePort > 0)
                 sktOpts.setPort(remotePort);
             I2PSocket i2ps = createI2PSocket(clientDest, sktOpts);
-            byte[] data = newRequest.toString().getBytes("ISO-8859-1");
             Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
-            new I2PTunnelHTTPClientRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
+            if (method.toUpperCase(Locale.US).equals("CONNECT")) {
+                byte[] data;
+                byte[] response;
+                if (usingWWWProxy) {
+                    data = newRequest.toString().getBytes("ISO-8859-1");
+                    response = null;
+                } else {
+                    data = null;
+                    response = I2PTunnelConnectClient.SUCCESS_RESPONSE;
+                }
+                new I2PTunnelRunner(s, i2ps, sockLock, data, response, mySockets, onTimeout);
+            } else {
+                byte[] data = newRequest.toString().getBytes("ISO-8859-1");
+                new I2PTunnelHTTPClientRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
+            }
         } catch (SocketException ex) {
             if (_log.shouldLog(Log.INFO)) {
                 _log.info(getPrefix(requestId) + "Error trying to connect", ex);
@@ -1310,8 +1354,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
         }
         }
          ****/
-        return protocol.toLowerCase(Locale.US).equals("http");
+        String lc = protocol.toLowerCase(Locale.US);
+        return lc.equals("http") || lc.equals("https");
     }
+
     private final static byte[] ERR_HELPER_DISABLED =
                                 ("HTTP/1.1 403 Disabled\r\n" +
             "Content-Type: text/plain\r\n" +
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelOutproxyRunner.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelOutproxyRunner.java
index daea6bd4f7b1a34dedbd404de8ab64cc065c32a4..a399a6ed0acc5a1e09b29f85c65e17b0afe4ffad 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelOutproxyRunner.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelOutproxyRunner.java
@@ -48,6 +48,7 @@ public class I2PTunnelOutproxyRunner extends I2PAppThread {
     private final Object slock, finishLock = new Object();
     volatile boolean finished = false;
     private final byte[] initialI2PData;
+    private final byte[] initialSocketData;
     /** when the last data was sent/received (or -1 if never) */
     private long lastActivityOn;
     /** when the runner started up */
@@ -68,11 +69,12 @@ public class I2PTunnelOutproxyRunner extends I2PAppThread {
                          it will be run before closing s.
      */
     public I2PTunnelOutproxyRunner(Socket s, Socket i2ps, Object slock, byte[] initialI2PData,
-                                   Runnable onTimeout) {
+                                   byte[] initialSocketData, Runnable onTimeout) {
         this.s = s;
         this.i2ps = i2ps;
         this.slock = slock;
         this.initialI2PData = initialI2PData;
+        this.initialSocketData = initialSocketData;
         this.onTimeout = onTimeout;
         lastActivityOn = -1;
         startedOn = Clock.getInstance().now();
@@ -130,9 +132,14 @@ public class I2PTunnelOutproxyRunner extends I2PAppThread {
                     i2pout.write(initialI2PData);
                     i2pout.flush();
             }
+            if (initialSocketData != null) {
+                // this does not increment totalReceived
+                out.write(initialSocketData);
+            }
             if (_log.shouldLog(Log.DEBUG))
                 _log.debug("Initial data " + (initialI2PData != null ? initialI2PData.length : 0) 
-                           + " written to the outproxy, starting forwarders");
+                           + " written to the outproxy, " + (initialSocketData != null ? initialSocketData.length : 0)
+                           + " written to the socket, starting forwarders");
             if (!(s instanceof InternalSocket))
                 in = new BufferedInputStream(in, 2*NETWORK_BUFFER_SIZE);
             Thread t1 = new StreamForwarder(in, i2pout, true);
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java
index 695f9aeb091d2107bf3dd7e84a2ea8315b3b32e4..b5f1e1c51f378bd31261df3ab6f41686102d19e3 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java
@@ -235,7 +235,7 @@ public class ConfigClientsHandler extends FormHandler {
             return;
         }
         ClientAppConfig ca = clients.get(i);
-        ClientApp clientApp = _context.clientAppManager().getClientApp(ca.className, LoadClientAppsJob.parseArgs(ca.args));
+        ClientApp clientApp = _context.routerAppManager().getClientApp(ca.className, LoadClientAppsJob.parseArgs(ca.args));
         if (clientApp != null && clientApp.getState() == ClientAppState.RUNNING) {
             try {
                 // todo parseArgs(ca.stopArgs) ?
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java
index 9ae98ce991c4bfea63e0605a5fd142d23bbd3137..3197dec5d9880ca3f572f65b41cd607e63d9c969 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java
@@ -103,7 +103,7 @@ public class ConfigClientsHelper extends HelperBase {
                 showStart = false;
                 showStop = false;
             } else {
-                ClientApp clientApp = _context.clientAppManager().getClientApp(ca.className, LoadClientAppsJob.parseArgs(ca.args));
+                ClientApp clientApp = _context.routerAppManager().getClientApp(ca.className, LoadClientAppsJob.parseArgs(ca.args));
                 showStart = clientApp == null;
                 showStop = clientApp != null && clientApp.getState() == ClientAppState.RUNNING;
             }
diff --git a/apps/routerconsole/jsp/debug.jsp b/apps/routerconsole/jsp/debug.jsp
index 93b9b58c07b9a21a85eed7dac95276bb8cf68210..f36e9f31ffa2b3cbc6176ba4be07976545c28364 100644
--- a/apps/routerconsole/jsp/debug.jsp
+++ b/apps/routerconsole/jsp/debug.jsp
@@ -35,7 +35,7 @@
     /*
      *  Print out the status for the AppManager
      */
-    ctx.clientAppManager().renderStatusHTML(out);
+    ctx.routerAppManager().renderStatusHTML(out);
 
     /*
      *  Print out the status for all the SessionKeyManagers
diff --git a/core/java/src/net/i2p/I2PAppContext.java b/core/java/src/net/i2p/I2PAppContext.java
index 9aaf4c3b2761c139b2750d48d32da2e19398c9ac..665b54feb9a74c8d6150b512e361c7ca2825810d 100644
--- a/core/java/src/net/i2p/I2PAppContext.java
+++ b/core/java/src/net/i2p/I2PAppContext.java
@@ -7,6 +7,7 @@ import java.util.Properties;
 import java.util.Random;
 import java.util.Set;
 
+import net.i2p.app.ClientAppManager;
 import net.i2p.client.naming.NamingService;
 import net.i2p.crypto.AESEngine;
 import net.i2p.crypto.CryptixAESEngine;
@@ -1028,37 +1029,11 @@ public class I2PAppContext {
     }
 
     /**
-     *  A local outproxy
-     *  @return The outproxy if it is registered, else null
-     *  @since 0.9.11
+     *  The RouterAppManager in RouterContext, null always in I2PAppContext
+     *  @return null always
+     *  @since 0.9.11, in RouterContext since 0.9.4
      */
-    public Outproxy outproxy() {
-        return _outproxy;
-    }
-
-    /**
-     *  Register as the outproxy. For now, only one.
-     *  @throws IllegalStateException if one was already registered
-     *  @since 0.9.11
-     */
-    public void registerOutproxy(Outproxy oproxy) {
-        synchronized(_lock21) {
-            if (_outproxy != null)
-                throw new IllegalStateException();
-            _outproxy = oproxy;
-        }
-    }
-
-    /**
-     *  Unregister the outproxy.
-     *  @throws IllegalStateException if it was not registered
-     *  @since 0.9.11
-     */
-    public void unregisterOutproxy(Outproxy oproxy) {
-        synchronized(_lock21) {
-            if (_outproxy != oproxy)
-                throw new IllegalStateException();
-            _outproxy = null;
-        }
+    public ClientAppManager clientAppManager() {
+        return null;
     }
 }
diff --git a/core/java/src/net/i2p/outproxy/Outproxy.java b/core/java/src/net/i2p/app/Outproxy.java
similarity index 74%
rename from core/java/src/net/i2p/outproxy/Outproxy.java
rename to core/java/src/net/i2p/app/Outproxy.java
index a18066acddb33cc62261069d107c5dd2f2e2b93b..9238cd37d81f9f382c5e94495ae325621e18f5b0 100644
--- a/core/java/src/net/i2p/outproxy/Outproxy.java
+++ b/core/java/src/net/i2p/app/Outproxy.java
@@ -1,4 +1,4 @@
-package net.i2p.outproxy;
+package net.i2p.app;
 
 import java.io.IOException;
 import java.net.Socket;
@@ -9,6 +9,8 @@ import java.net.Socket;
  */
 public interface Outproxy {
 
+    public static final String NAME = "outproxy";
+
     /**
      *
      */
diff --git a/router/java/src/net/i2p/router/RouterContext.java b/router/java/src/net/i2p/router/RouterContext.java
index 6af37e54fa0dba00051d43e6669014016ce37498..ba837ccf2de7ba4623f0a38b068a02d7d812f4dd 100644
--- a/router/java/src/net/i2p/router/RouterContext.java
+++ b/router/java/src/net/i2p/router/RouterContext.java
@@ -8,6 +8,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 import net.i2p.I2PAppContext;
+import net.i2p.app.ClientAppManager;
 import net.i2p.data.Hash;
 import net.i2p.data.RouterInfo;
 import net.i2p.internal.InternalClientManager;
@@ -586,7 +587,18 @@ public class RouterContext extends I2PAppContext {
      *  @return the manager
      *  @since 0.9.4
      */
-    public RouterAppManager clientAppManager() {
+    @Override
+    public ClientAppManager clientAppManager() {
+        return _appManager;
+    }
+
+    /**
+     *  The RouterAppManager.
+     *  For convenience, same as clientAppManager(), no cast required
+     *  @return the manager
+     *  @since 0.9.11
+     */
+    public RouterAppManager routerAppManager() {
         return _appManager;
     }
 }
diff --git a/router/java/src/net/i2p/router/startup/LoadClientAppsJob.java b/router/java/src/net/i2p/router/startup/LoadClientAppsJob.java
index 2cd538fe16ff0d9d8db684e3206170d400b70cb5..27242397e82a4504563036fcd4ee41878b04d152 100644
--- a/router/java/src/net/i2p/router/startup/LoadClientAppsJob.java
+++ b/router/java/src/net/i2p/router/startup/LoadClientAppsJob.java
@@ -267,13 +267,13 @@ public class LoadClientAppsJob extends JobImpl {
                 Class<?> cls = Class.forName(_className, true, _cl);
                 if (isRouterApp(cls)) {
                     Constructor<?> con = cls.getConstructor(RouterContext.class, ClientAppManager.class, String[].class);
-                    RouterAppManager mgr = _ctx.clientAppManager();
+                    RouterAppManager mgr = _ctx.routerAppManager();
                     Object[] conArgs = new Object[] {_ctx, _ctx.clientAppManager(), _args};
                     RouterApp app = (RouterApp) con.newInstance(conArgs);
                     mgr.addAndStart(app, _args);
                 } else if (isClientApp(cls)) {
                     Constructor<?> con = cls.getConstructor(I2PAppContext.class, ClientAppManager.class, String[].class);
-                    RouterAppManager mgr = _ctx.clientAppManager();
+                    RouterAppManager mgr = _ctx.routerAppManager();
                     Object[] conArgs = new Object[] {_ctx, _ctx.clientAppManager(), _args};
                     ClientApp app = (ClientApp) con.newInstance(conArgs);
                     mgr.addAndStart(app, _args);