< prev index next >
test/jdk/java/net/httpclient/ProxyServer.java
Print this page
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
@@ -23,10 +23,13 @@
import java.net.*;
import java.io.*;
import java.util.*;
import java.security.*;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Arrays.asList;
+import static java.util.stream.Collectors.toList;
/**
* A minimal proxy server that supports CONNECT tunneling. It does not do
* any header transformations. In future this could be added.
* Two threads are created per client connection. So, it's not
@@ -35,32 +38,67 @@
public class ProxyServer extends Thread implements Closeable {
ServerSocket listener;
int port;
volatile boolean debug;
+ private final Credentials credentials; // may be null
+
+ private static class Credentials {
+ private final String name;
+ private final String password;
+ private Credentials(String name, String password) {
+ this.name = name;
+ this.password = password;
+ }
+ public String name() { return name; }
+ public String password() { return password; }
+ }
/**
* Create proxy on port (zero means don't care). Call getPort()
* to get the assigned port.
*/
public ProxyServer(Integer port) throws IOException {
this(port, false);
}
- public ProxyServer(Integer port, Boolean debug) throws IOException {
+ public ProxyServer(Integer port,
+ Boolean debug,
+ String username,
+ String password)
+ throws IOException
+ {
+ this(port, debug, new Credentials(username, password));
+ }
+
+ public ProxyServer(Integer port,
+ Boolean debug)
+ throws IOException
+ {
+ this(port, debug, null);
+ }
+
+ public ProxyServer(Integer port,
+ Boolean debug,
+ Credentials credentials)
+ throws IOException
+ {
this.debug = debug;
listener = new ServerSocket();
listener.setReuseAddress(false);
listener.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), port));
this.port = listener.getLocalPort();
+ this.credentials = credentials;
setName("ProxyListener");
setDaemon(true);
connections = new LinkedList<>();
start();
}
- public ProxyServer(String s) { }
+ public ProxyServer(String s) {
+ credentials = null;
+ }
/**
* Returns the port number this proxy is listening on
*/
public int getPort() {
@@ -192,20 +230,73 @@
}
}
return -1;
}
+ // Checks credentials in the request against those allowable by the proxy.
+ private boolean authorized(Credentials credentials,
+ List<String> requestHeaders) {
+ List<String> authorization = requestHeaders.stream()
+ .filter(n -> n.toLowerCase(Locale.US).startsWith("proxy-authorization"))
+ .collect(toList());
+
+ if (authorization.isEmpty())
+ return false;
+
+ if (authorization.size() != 1) {
+ throw new IllegalStateException("Authorization unexpected count:" + authorization);
+ }
+ String value = authorization.get(0).substring("proxy-authorization".length()).trim();
+ if (!value.startsWith(":"))
+ throw new IllegalStateException("Authorization malformed: " + value);
+ value = value.substring(1).trim();
+
+ if (!value.startsWith("Basic "))
+ throw new IllegalStateException("Authorization not Basic: " + value);
+
+ value = value.substring("Basic ".length());
+ String values = new String(Base64.getDecoder().decode(value), UTF_8);
+ int sep = values.indexOf(':');
+ if (sep < 1) {
+ throw new IllegalStateException("Authorization no colon: " + values);
+ }
+ String name = values.substring(0, sep);
+ String password = values.substring(sep + 1);
+
+ if (name.equals(credentials.name()) && password.equals(credentials.password()))
+ return true;
+
+ return false;
+ }
+
public void init() {
try {
- byte[] buf = readHeaders(clientIn);
- int p = findCRLF(buf);
- if (p == -1) {
+ byte[] buf;
+ while (true) {
+ buf = readHeaders(clientIn);
+ if (findCRLF(buf) == -1) {
close();
return;
}
+
+ List<String> headers = asList(new String(buf, UTF_8).split("\r\n"));
+ // check authorization credentials, if required by the server
+ if (credentials != null && !authorized(credentials, headers)) {
+ String resp = "HTTP/1.1 407 Proxy Authentication Required\r\n" +
+ "Content-Length: 0\r\n" +
+ "Proxy-Authenticate: Basic realm=\"proxy realm\"\r\n\r\n";
+
+ clientOut.write(resp.getBytes(UTF_8));
+ } else {
+ break;
+ }
+ }
+
+ int p = findCRLF(buf);
String cmd = new String(buf, 0, p, "US-ASCII");
String[] params = cmd.split(" ");
+
if (params[0].equals("CONNECT")) {
doTunnel(params[1]);
} else {
doProxy(params[1], buf, p, cmd);
}
< prev index next >