< prev index next >

test/jdk/java/net/httpclient/ProxyServer.java

Print this page


   1 /*
   2  * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.net.*;
  25 import java.io.*;
  26 import java.util.*;
  27 import java.security.*;



  28 
  29 /**
  30  * A minimal proxy server that supports CONNECT tunneling. It does not do
  31  * any header transformations. In future this could be added.
  32  * Two threads are created per client connection. So, it's not
  33  * intended for large numbers of parallel connections.
  34  */
  35 public class ProxyServer extends Thread implements Closeable {
  36 
  37     ServerSocket listener;
  38     int port;
  39     volatile boolean debug;












  40 
  41     /**
  42      * Create proxy on port (zero means don't care). Call getPort()
  43      * to get the assigned port.
  44      */
  45     public ProxyServer(Integer port) throws IOException {
  46         this(port, false);
  47     }
  48 
  49     public ProxyServer(Integer port, Boolean debug) throws IOException {




















  50         this.debug = debug;
  51         listener = new ServerSocket();
  52         listener.setReuseAddress(false);
  53         listener.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), port));
  54         this.port = listener.getLocalPort();

  55         setName("ProxyListener");
  56         setDaemon(true);
  57         connections = new LinkedList<>();
  58         start();
  59     }
  60 
  61     public ProxyServer(String s) {  }


  62 
  63     /**
  64      * Returns the port number this proxy is listening on
  65      */
  66     public int getPort() {
  67         return port;
  68     }
  69 
  70     /**
  71      * Shuts down the proxy, probably aborting any connections
  72      * currently open
  73      */
  74     public void close() throws IOException {
  75         if (debug) System.out.println("Proxy: closing");
  76         done = true;
  77         listener.close();
  78         for (Connection c : connections) {
  79             c.close();
  80         }
  81     }


 177             return out.isAlive() || in.isAlive();
 178         }
 179 
 180         private volatile boolean closing;
 181         public synchronized void close() throws IOException {
 182             closing = true;
 183             if (debug) System.out.println("Closing connection (proxy)");
 184             if (serverSocket != null) serverSocket.close();
 185             if (clientSocket != null) clientSocket.close();
 186         }
 187 
 188         int findCRLF(byte[] b) {
 189             for (int i=0; i<b.length-1; i++) {
 190                 if (b[i] == CR && b[i+1] == LF) {
 191                     return i;
 192                 }
 193             }
 194             return -1;
 195         }
 196 




































 197         public void init() {
 198             try {
 199                 byte[] buf = readHeaders(clientIn);
 200                 int p = findCRLF(buf);
 201                 if (p == -1) {

 202                     close();
 203                     return;
 204                 }















 205                 String cmd = new String(buf, 0, p, "US-ASCII");
 206                 String[] params = cmd.split(" ");

 207                 if (params[0].equals("CONNECT")) {
 208                     doTunnel(params[1]);
 209                 } else {
 210                     doProxy(params[1], buf, p, cmd);
 211                 }
 212             } catch (Throwable e) {
 213                 if (debug) {
 214                     System.out.println (e);
 215                 }
 216                 try {close(); } catch (IOException e1) {}
 217             }
 218         }
 219 
 220         void doProxy(String dest, byte[] buf, int p, String cmdLine)
 221             throws IOException
 222         {
 223             try {
 224                 URI uri = new URI(dest);
 225                 if (!uri.isAbsolute()) {
 226                     throw new IOException("request URI not absolute");


   1 /*
   2  * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.net.*;
  25 import java.io.*;
  26 import java.util.*;
  27 import java.security.*;
  28 import static java.nio.charset.StandardCharsets.UTF_8;
  29 import static java.util.Arrays.asList;
  30 import static java.util.stream.Collectors.toList;
  31 
  32 /**
  33  * A minimal proxy server that supports CONNECT tunneling. It does not do
  34  * any header transformations. In future this could be added.
  35  * Two threads are created per client connection. So, it's not
  36  * intended for large numbers of parallel connections.
  37  */
  38 public class ProxyServer extends Thread implements Closeable {
  39 
  40     ServerSocket listener;
  41     int port;
  42     volatile boolean debug;
  43     private final Credentials credentials;  // may be null
  44 
  45     private static class Credentials {
  46         private final String name;
  47         private final String password;
  48         private Credentials(String name, String password) {
  49             this.name = name;
  50             this.password = password;
  51         }
  52         public String name() { return name; }
  53         public String password() { return password; }
  54     }
  55 
  56     /**
  57      * Create proxy on port (zero means don't care). Call getPort()
  58      * to get the assigned port.
  59      */
  60     public ProxyServer(Integer port) throws IOException {
  61         this(port, false);
  62     }
  63 
  64     public ProxyServer(Integer port,
  65                        Boolean debug,
  66                        String username,
  67                        String password)
  68         throws IOException
  69     {
  70         this(port, debug, new Credentials(username, password));
  71     }
  72 
  73     public ProxyServer(Integer port,
  74                        Boolean debug)
  75         throws IOException
  76     {
  77         this(port, debug, null);
  78     }
  79 
  80     public ProxyServer(Integer port,
  81                        Boolean debug,
  82                        Credentials credentials)
  83         throws IOException
  84     {
  85         this.debug = debug;
  86         listener = new ServerSocket();
  87         listener.setReuseAddress(false);
  88         listener.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), port));
  89         this.port = listener.getLocalPort();
  90         this.credentials = credentials;
  91         setName("ProxyListener");
  92         setDaemon(true);
  93         connections = new LinkedList<>();
  94         start();
  95     }
  96 
  97     public ProxyServer(String s) {
  98         credentials = null;
  99     }
 100 
 101     /**
 102      * Returns the port number this proxy is listening on
 103      */
 104     public int getPort() {
 105         return port;
 106     }
 107 
 108     /**
 109      * Shuts down the proxy, probably aborting any connections
 110      * currently open
 111      */
 112     public void close() throws IOException {
 113         if (debug) System.out.println("Proxy: closing");
 114         done = true;
 115         listener.close();
 116         for (Connection c : connections) {
 117             c.close();
 118         }
 119     }


 215             return out.isAlive() || in.isAlive();
 216         }
 217 
 218         private volatile boolean closing;
 219         public synchronized void close() throws IOException {
 220             closing = true;
 221             if (debug) System.out.println("Closing connection (proxy)");
 222             if (serverSocket != null) serverSocket.close();
 223             if (clientSocket != null) clientSocket.close();
 224         }
 225 
 226         int findCRLF(byte[] b) {
 227             for (int i=0; i<b.length-1; i++) {
 228                 if (b[i] == CR && b[i+1] == LF) {
 229                     return i;
 230                 }
 231             }
 232             return -1;
 233         }
 234 
 235         // Checks credentials in the request against those allowable by the proxy.
 236         private boolean authorized(Credentials credentials,
 237                                    List<String> requestHeaders) {
 238             List<String> authorization = requestHeaders.stream()
 239                     .filter(n -> n.toLowerCase(Locale.US).startsWith("proxy-authorization"))
 240                     .collect(toList());
 241 
 242             if (authorization.isEmpty())
 243                 return false;
 244 
 245             if (authorization.size() != 1) {
 246                 throw new IllegalStateException("Authorization unexpected count:" + authorization);
 247             }
 248             String value = authorization.get(0).substring("proxy-authorization".length()).trim();
 249             if (!value.startsWith(":"))
 250                 throw new IllegalStateException("Authorization malformed: " + value);
 251             value = value.substring(1).trim();
 252 
 253             if (!value.startsWith("Basic "))
 254                 throw new IllegalStateException("Authorization not Basic: " + value);
 255 
 256             value = value.substring("Basic ".length());
 257             String values = new String(Base64.getDecoder().decode(value), UTF_8);
 258             int sep = values.indexOf(':');
 259             if (sep < 1) {
 260                 throw new IllegalStateException("Authorization no colon: " +  values);
 261             }
 262             String name = values.substring(0, sep);
 263             String password = values.substring(sep + 1);
 264 
 265             if (name.equals(credentials.name()) && password.equals(credentials.password()))
 266                 return true;
 267 
 268             return false;
 269         }
 270 
 271         public void init() {
 272             try {
 273                 byte[] buf;
 274                 while (true) {
 275                     buf = readHeaders(clientIn);
 276                     if (findCRLF(buf) == -1) {
 277                         close();
 278                         return;
 279                     }
 280 
 281                     List<String> headers = asList(new String(buf, UTF_8).split("\r\n"));
 282                     // check authorization credentials, if required by the server
 283                     if (credentials != null && !authorized(credentials, headers)) {
 284                         String resp = "HTTP/1.1 407 Proxy Authentication Required\r\n" +
 285                                       "Content-Length: 0\r\n" +
 286                                       "Proxy-Authenticate: Basic realm=\"proxy realm\"\r\n\r\n";
 287 
 288                         clientOut.write(resp.getBytes(UTF_8));
 289                     } else {
 290                         break;
 291                     }
 292                 }
 293 
 294                 int p = findCRLF(buf);
 295                 String cmd = new String(buf, 0, p, "US-ASCII");
 296                 String[] params = cmd.split(" ");
 297 
 298                 if (params[0].equals("CONNECT")) {
 299                     doTunnel(params[1]);
 300                 } else {
 301                     doProxy(params[1], buf, p, cmd);
 302                 }
 303             } catch (Throwable e) {
 304                 if (debug) {
 305                     System.out.println (e);
 306                 }
 307                 try {close(); } catch (IOException e1) {}
 308             }
 309         }
 310 
 311         void doProxy(String dest, byte[] buf, int p, String cmdLine)
 312             throws IOException
 313         {
 314             try {
 315                 URI uri = new URI(dest);
 316                 if (!uri.isAbsolute()) {
 317                     throw new IOException("request URI not absolute");


< prev index next >