8187443: Forest Consolidation: Move files to unified layout

Reviewed-by: darcy, ihse
This commit is contained in:
Erik Joelsson 2017-09-12 19:03:39 +02:00
parent 270fe13182
commit 3789983e89
56923 changed files with 3 additions and 15727 deletions

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 1996, 2008, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www;
/**
* An exception thrown by the MimeLauncher when it is unable to launch
* an external content viewer.
*
* @author Sunita Mani
*/
public class ApplicationLaunchException extends Exception {
private static final long serialVersionUID = -4782286141289536883L;
public ApplicationLaunchException(String reason) {
super(reason);
}
}

View file

@ -0,0 +1,265 @@
/*
* Copyright (c) 1996, 2011, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www;
import java.util.Iterator;
/* This is useful for the nightmare of parsing multi-part HTTP/RFC822 headers
* sensibly:
* From a String like: 'timeout=15, max=5'
* create an array of Strings:
* { {"timeout", "15"},
* {"max", "5"}
* }
* From one like: 'Basic Realm="FuzzFace" Foo="Biz Bar Baz"'
* create one like (no quotes in literal):
* { {"basic", null},
* {"realm", "FuzzFace"}
* {"foo", "Biz Bar Baz"}
* }
* keys are converted to lower case, vals are left as is....
*
* @author Dave Brown
*/
public class HeaderParser {
/* table of key/val pairs */
String raw;
String[][] tab;
int nkeys;
int asize = 10; // initial size of array is 10
public HeaderParser(String raw) {
this.raw = raw;
tab = new String[asize][2];
parse();
}
private HeaderParser () {
}
/**
* create a new HeaderParser from this, whose keys (and corresponding values)
* range from "start" to "end-1"
*/
public HeaderParser subsequence (int start, int end) {
if (start == 0 && end == nkeys) {
return this;
}
if (start < 0 || start >= end || end > nkeys)
throw new IllegalArgumentException ("invalid start or end");
HeaderParser n = new HeaderParser ();
n.tab = new String [asize][2];
n.asize = asize;
System.arraycopy (tab, start, n.tab, 0, (end-start));
n.nkeys= (end-start);
return n;
}
private void parse() {
if (raw != null) {
raw = raw.trim();
char[] ca = raw.toCharArray();
int beg = 0, end = 0, i = 0;
boolean inKey = true;
boolean inQuote = false;
int len = ca.length;
while (end < len) {
char c = ca[end];
if ((c == '=') && !inQuote) { // end of a key
tab[i][0] = new String(ca, beg, end-beg).toLowerCase();
inKey = false;
end++;
beg = end;
} else if (c == '\"') {
if (inQuote) {
tab[i++][1]= new String(ca, beg, end-beg);
inQuote=false;
do {
end++;
} while (end < len && (ca[end] == ' ' || ca[end] == ','));
inKey=true;
beg=end;
} else {
inQuote=true;
end++;
beg=end;
}
} else if (c == ' ' || c == ',') { // end key/val, of whatever we're in
if (inQuote) {
end++;
continue;
} else if (inKey) {
tab[i++][0] = (new String(ca, beg, end-beg)).toLowerCase();
} else {
tab[i++][1] = (new String(ca, beg, end-beg));
}
while (end < len && (ca[end] == ' ' || ca[end] == ',')) {
end++;
}
inKey = true;
beg = end;
} else {
end++;
}
if (i == asize) {
asize = asize * 2;
String[][] ntab = new String[asize][2];
System.arraycopy (tab, 0, ntab, 0, tab.length);
tab = ntab;
}
}
// get last key/val, if any
if (--end > beg) {
if (!inKey) {
if (ca[end] == '\"') {
tab[i++][1] = (new String(ca, beg, end-beg));
} else {
tab[i++][1] = (new String(ca, beg, end-beg+1));
}
} else {
tab[i++][0] = (new String(ca, beg, end-beg+1)).toLowerCase();
}
} else if (end == beg) {
if (!inKey) {
if (ca[end] == '\"') {
tab[i++][1] = String.valueOf(ca[end-1]);
} else {
tab[i++][1] = String.valueOf(ca[end]);
}
} else {
tab[i++][0] = String.valueOf(ca[end]).toLowerCase();
}
}
nkeys=i;
}
}
public String findKey(int i) {
if (i < 0 || i > asize)
return null;
return tab[i][0];
}
public String findValue(int i) {
if (i < 0 || i > asize)
return null;
return tab[i][1];
}
public String findValue(String key) {
return findValue(key, null);
}
public String findValue(String k, String Default) {
if (k == null)
return Default;
k = k.toLowerCase();
for (int i = 0; i < asize; ++i) {
if (tab[i][0] == null) {
return Default;
} else if (k.equals(tab[i][0])) {
return tab[i][1];
}
}
return Default;
}
class ParserIterator implements Iterator<String> {
int index;
boolean returnsValue; // or key
ParserIterator (boolean returnValue) {
returnsValue = returnValue;
}
public boolean hasNext () {
return index<nkeys;
}
public String next () {
return tab[index++][returnsValue?1:0];
}
public void remove () {
throw new UnsupportedOperationException ("remove not supported");
}
}
public Iterator<String> keys () {
return new ParserIterator (false);
}
public Iterator<String> values () {
return new ParserIterator (true);
}
public String toString () {
Iterator<String> k = keys();
StringBuilder sb = new StringBuilder();
sb.append("{size=").append(asize).append(" nkeys=").append(nkeys)
.append(' ');
for (int i=0; k.hasNext(); i++) {
String key = k.next();
String val = findValue (i);
if (val != null && "".equals (val)) {
val = null;
}
sb.append(" {").append(key).append(val == null ? "" : "," + val)
.append('}');
if (k.hasNext()) {
sb.append (',');
}
}
sb.append (" }");
return sb.toString();
}
public int findInt(String k, int Default) {
try {
return Integer.parseInt(findValue(k, String.valueOf(Default)));
} catch (Throwable t) {
return Default;
}
}
/*
public static void main(String[] a) throws Exception {
System.out.print("enter line to parse> ");
System.out.flush();
DataInputStream dis = new DataInputStream(System.in);
String line = dis.readLine();
HeaderParser p = new HeaderParser(line);
for (int i = 0; i < asize; ++i) {
if (p.findKey(i) == null) break;
String v = p.findValue(i);
System.out.println(i + ") " +p.findKey(i) + "="+v);
}
System.out.println("Done!");
}
*/
}

View file

@ -0,0 +1,522 @@
/*
* Copyright (c) 1995, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*-
* news stream opener
*/
package sun.net.www;
import java.io.*;
import java.util.Collections;
import java.util.*;
/** An RFC 844 or MIME message header. Includes methods
for parsing headers from incoming streams, fetching
values, setting values, and printing headers.
Key values of null are legal: they indicate lines in
the header that don't have a valid key, but do have
a value (this isn't legal according to the standard,
but lines like this are everywhere). */
public
class MessageHeader {
private String keys[];
private String values[];
private int nkeys;
public MessageHeader () {
grow();
}
public MessageHeader (InputStream is) throws java.io.IOException {
parseHeader(is);
}
/**
* Returns list of header names in a comma separated list
*/
public synchronized String getHeaderNamesInList() {
StringJoiner joiner = new StringJoiner(",");
for (int i=0; i<nkeys; i++) {
joiner.add(keys[i]);
}
return joiner.toString();
}
/**
* Reset a message header (all key/values removed)
*/
public synchronized void reset() {
keys = null;
values = null;
nkeys = 0;
grow();
}
/**
* Find the value that corresponds to this key.
* It finds only the first occurrence of the key.
* @param k the key to find.
* @return null if not found.
*/
public synchronized String findValue(String k) {
if (k == null) {
for (int i = nkeys; --i >= 0;)
if (keys[i] == null)
return values[i];
} else
for (int i = nkeys; --i >= 0;) {
if (k.equalsIgnoreCase(keys[i]))
return values[i];
}
return null;
}
// return the location of the key
public synchronized int getKey(String k) {
for (int i = nkeys; --i >= 0;)
if ((keys[i] == k) ||
(k != null && k.equalsIgnoreCase(keys[i])))
return i;
return -1;
}
public synchronized String getKey(int n) {
if (n < 0 || n >= nkeys) return null;
return keys[n];
}
public synchronized String getValue(int n) {
if (n < 0 || n >= nkeys) return null;
return values[n];
}
/** Deprecated: Use multiValueIterator() instead.
*
* Find the next value that corresponds to this key.
* It finds the first value that follows v. To iterate
* over all the values of a key use:
* <pre>
* for(String v=h.findValue(k); v!=null; v=h.findNextValue(k, v)) {
* ...
* }
* </pre>
*/
public synchronized String findNextValue(String k, String v) {
boolean foundV = false;
if (k == null) {
for (int i = nkeys; --i >= 0;)
if (keys[i] == null)
if (foundV)
return values[i];
else if (values[i] == v)
foundV = true;
} else
for (int i = nkeys; --i >= 0;)
if (k.equalsIgnoreCase(keys[i]))
if (foundV)
return values[i];
else if (values[i] == v)
foundV = true;
return null;
}
/**
* Removes bare Negotiate and Kerberos headers when an "NTLM ..."
* appears. All Performed on headers with key being k.
* @return true if there is a change
*/
public boolean filterNTLMResponses(String k) {
boolean found = false;
for (int i=0; i<nkeys; i++) {
if (k.equalsIgnoreCase(keys[i])
&& values[i] != null && values[i].length() > 5
&& values[i].substring(0, 5).equalsIgnoreCase("NTLM ")) {
found = true;
break;
}
}
if (found) {
int j = 0;
for (int i=0; i<nkeys; i++) {
if (k.equalsIgnoreCase(keys[i]) && (
"Negotiate".equalsIgnoreCase(values[i]) ||
"Kerberos".equalsIgnoreCase(values[i]))) {
continue;
}
if (i != j) {
keys[j] = keys[i];
values[j] = values[i];
}
j++;
}
if (j != nkeys) {
nkeys = j;
return true;
}
}
return false;
}
class HeaderIterator implements Iterator<String> {
int index = 0;
int next = -1;
String key;
boolean haveNext = false;
Object lock;
public HeaderIterator (String k, Object lock) {
key = k;
this.lock = lock;
}
public boolean hasNext () {
synchronized (lock) {
if (haveNext) {
return true;
}
while (index < nkeys) {
if (key.equalsIgnoreCase (keys[index])) {
haveNext = true;
next = index++;
return true;
}
index ++;
}
return false;
}
}
public String next() {
synchronized (lock) {
if (haveNext) {
haveNext = false;
return values [next];
}
if (hasNext()) {
return next();
} else {
throw new NoSuchElementException ("No more elements");
}
}
}
public void remove () {
throw new UnsupportedOperationException ("remove not allowed");
}
}
/**
* return an Iterator that returns all values of a particular
* key in sequence
*/
public Iterator<String> multiValueIterator (String k) {
return new HeaderIterator (k, this);
}
public synchronized Map<String, List<String>> getHeaders() {
return getHeaders(null);
}
public synchronized Map<String, List<String>> getHeaders(String[] excludeList) {
return filterAndAddHeaders(excludeList, null);
}
public synchronized Map<String, List<String>> filterAndAddHeaders(
String[] excludeList, Map<String, List<String>> include) {
boolean skipIt = false;
Map<String, List<String>> m = new HashMap<>();
for (int i = nkeys; --i >= 0;) {
if (excludeList != null) {
// check if the key is in the excludeList.
// if so, don't include it in the Map.
for (int j = 0; j < excludeList.length; j++) {
if ((excludeList[j] != null) &&
(excludeList[j].equalsIgnoreCase(keys[i]))) {
skipIt = true;
break;
}
}
}
if (!skipIt) {
List<String> l = m.get(keys[i]);
if (l == null) {
l = new ArrayList<>();
m.put(keys[i], l);
}
l.add(values[i]);
} else {
// reset the flag
skipIt = false;
}
}
if (include != null) {
for (Map.Entry<String,List<String>> entry: include.entrySet()) {
List<String> l = m.get(entry.getKey());
if (l == null) {
l = new ArrayList<>();
m.put(entry.getKey(), l);
}
l.addAll(entry.getValue());
}
}
for (String key : m.keySet()) {
m.put(key, Collections.unmodifiableList(m.get(key)));
}
return Collections.unmodifiableMap(m);
}
/** Prints the key-value pairs represented by this
header. Also prints the RFC required blank line
at the end. Omits pairs with a null key. */
public synchronized void print(PrintStream p) {
for (int i = 0; i < nkeys; i++)
if (keys[i] != null) {
p.print(keys[i] +
(values[i] != null ? ": "+values[i]: "") + "\r\n");
}
p.print("\r\n");
p.flush();
}
/** Adds a key value pair to the end of the
header. Duplicates are allowed */
public synchronized void add(String k, String v) {
grow();
keys[nkeys] = k;
values[nkeys] = v;
nkeys++;
}
/** Prepends a key value pair to the beginning of the
header. Duplicates are allowed */
public synchronized void prepend(String k, String v) {
grow();
for (int i = nkeys; i > 0; i--) {
keys[i] = keys[i-1];
values[i] = values[i-1];
}
keys[0] = k;
values[0] = v;
nkeys++;
}
/** Overwrite the previous key/val pair at location 'i'
* with the new k/v. If the index didn't exist before
* the key/val is simply tacked onto the end.
*/
public synchronized void set(int i, String k, String v) {
grow();
if (i < 0) {
return;
} else if (i >= nkeys) {
add(k, v);
} else {
keys[i] = k;
values[i] = v;
}
}
/** grow the key/value arrays as needed */
private void grow() {
if (keys == null || nkeys >= keys.length) {
String[] nk = new String[nkeys + 4];
String[] nv = new String[nkeys + 4];
if (keys != null)
System.arraycopy(keys, 0, nk, 0, nkeys);
if (values != null)
System.arraycopy(values, 0, nv, 0, nkeys);
keys = nk;
values = nv;
}
}
/**
* Remove the key from the header. If there are multiple values under
* the same key, they are all removed.
* Nothing is done if the key doesn't exist.
* After a remove, the other pairs' order are not changed.
* @param k the key to remove
*/
public synchronized void remove(String k) {
if(k == null) {
for (int i = 0; i < nkeys; i++) {
while (keys[i] == null && i < nkeys) {
for(int j=i; j<nkeys-1; j++) {
keys[j] = keys[j+1];
values[j] = values[j+1];
}
nkeys--;
}
}
} else {
for (int i = 0; i < nkeys; i++) {
while (k.equalsIgnoreCase(keys[i]) && i < nkeys) {
for(int j=i; j<nkeys-1; j++) {
keys[j] = keys[j+1];
values[j] = values[j+1];
}
nkeys--;
}
}
}
}
/** Sets the value of a key. If the key already
exists in the header, it's value will be
changed. Otherwise a new key/value pair will
be added to the end of the header. */
public synchronized void set(String k, String v) {
for (int i = nkeys; --i >= 0;)
if (k.equalsIgnoreCase(keys[i])) {
values[i] = v;
return;
}
add(k, v);
}
/** Set's the value of a key only if there is no
* key with that value already.
*/
public synchronized void setIfNotSet(String k, String v) {
if (findValue(k) == null) {
add(k, v);
}
}
/** Convert a message-id string to canonical form (strips off
leading and trailing {@literal <>s}) */
public static String canonicalID(String id) {
if (id == null)
return "";
int st = 0;
int len = id.length();
boolean substr = false;
int c;
while (st < len && ((c = id.charAt(st)) == '<' ||
c <= ' ')) {
st++;
substr = true;
}
while (st < len && ((c = id.charAt(len - 1)) == '>' ||
c <= ' ')) {
len--;
substr = true;
}
return substr ? id.substring(st, len) : id;
}
/** Parse a MIME header from an input stream. */
public void parseHeader(InputStream is) throws java.io.IOException {
synchronized (this) {
nkeys = 0;
}
mergeHeader(is);
}
/** Parse and merge a MIME header from an input stream. */
@SuppressWarnings("fallthrough")
public void mergeHeader(InputStream is) throws java.io.IOException {
if (is == null)
return;
char s[] = new char[10];
int firstc = is.read();
while (firstc != '\n' && firstc != '\r' && firstc >= 0) {
int len = 0;
int keyend = -1;
int c;
boolean inKey = firstc > ' ';
s[len++] = (char) firstc;
parseloop:{
while ((c = is.read()) >= 0) {
switch (c) {
case ':':
if (inKey && len > 0)
keyend = len;
inKey = false;
break;
case '\t':
c = ' ';
/*fall through*/
case ' ':
inKey = false;
break;
case '\r':
case '\n':
firstc = is.read();
if (c == '\r' && firstc == '\n') {
firstc = is.read();
if (firstc == '\r')
firstc = is.read();
}
if (firstc == '\n' || firstc == '\r' || firstc > ' ')
break parseloop;
/* continuation */
c = ' ';
break;
}
if (len >= s.length) {
char ns[] = new char[s.length * 2];
System.arraycopy(s, 0, ns, 0, len);
s = ns;
}
s[len++] = (char) c;
}
firstc = -1;
}
while (len > 0 && s[len - 1] <= ' ')
len--;
String k;
if (keyend <= 0) {
k = null;
keyend = 0;
} else {
k = String.copyValueOf(s, 0, keyend);
if (keyend < len && s[keyend] == ':')
keyend++;
while (keyend < len && s[keyend] <= ' ')
keyend++;
}
String v;
if (keyend >= len)
v = new String();
else
v = String.copyValueOf(s, keyend, len - keyend);
add(k, v);
}
}
public synchronized String toString() {
String result = super.toString() + nkeys + " pairs: ";
for (int i = 0; i < keys.length && i < nkeys; i++) {
result += "{"+keys[i]+": "+values[i]+"}";
}
return result;
}
}

View file

@ -0,0 +1,218 @@
/*
* Copyright (c) 1994, 2017, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www;
import java.net.URL;
import java.util.*;
import java.io.*;
import sun.net.ProgressSource;
import sun.net.www.http.ChunkedInputStream;
public class MeteredStream extends FilterInputStream {
// Instance variables.
/* if expected != -1, after we've read >= expected, we're "closed" and return -1
* from subsequest read() 's
*/
protected boolean closed = false;
protected long expected;
protected long count = 0;
protected long markedCount = 0;
protected int markLimit = -1;
protected ProgressSource pi;
public MeteredStream(InputStream is, ProgressSource pi, long expected)
{
super(is);
this.pi = pi;
this.expected = expected;
if (pi != null) {
pi.updateProgress(0, expected);
}
}
private final void justRead(long n) throws IOException {
if (n == -1) {
/*
* don't close automatically when mark is set and is valid;
* cannot reset() after close()
*/
if (!isMarked()) {
close();
}
return;
}
count += n;
/**
* If read beyond the markLimit, invalidate the mark
*/
if (count - markedCount > markLimit) {
markLimit = -1;
}
if (pi != null)
pi.updateProgress(count, expected);
if (isMarked()) {
return;
}
// if expected length is known, we could determine if
// read overrun.
if (expected > 0) {
if (count >= expected) {
close();
}
}
}
/**
* Returns true if the mark is valid, false otherwise
*/
private boolean isMarked() {
if (markLimit < 0) {
return false;
}
// mark is set, but is not valid anymore
if (count - markedCount > markLimit) {
return false;
}
// mark still holds
return true;
}
public synchronized int read() throws java.io.IOException {
if (closed) {
return -1;
}
int c = in.read();
if (c != -1) {
justRead(1);
} else {
justRead(c);
}
return c;
}
public synchronized int read(byte b[], int off, int len)
throws java.io.IOException {
if (closed) {
return -1;
}
int n = in.read(b, off, len);
justRead(n);
return n;
}
public synchronized long skip(long n) throws IOException {
// REMIND: what does skip do on EOF????
if (closed) {
return 0;
}
if (in instanceof ChunkedInputStream) {
n = in.skip(n);
}
else {
// just skip min(n, num_bytes_left)
long min = (n > expected - count) ? expected - count: n;
n = in.skip(min);
}
justRead(n);
return n;
}
public void close() throws IOException {
if (closed) {
return;
}
if (pi != null)
pi.finishTracking();
closed = true;
in.close();
}
public synchronized int available() throws IOException {
return closed ? 0: in.available();
}
public synchronized void mark(int readLimit) {
if (closed) {
return;
}
super.mark(readLimit);
/*
* mark the count to restore upon reset
*/
markedCount = count;
markLimit = readLimit;
}
public synchronized void reset() throws IOException {
if (closed) {
return;
}
if (!isMarked()) {
throw new IOException ("Resetting to an invalid mark");
}
count = markedCount;
super.reset();
}
public boolean markSupported() {
if (closed) {
return false;
}
return super.markSupported();
}
@SuppressWarnings("deprecation")
protected void finalize() throws Throwable {
try {
close();
if (pi != null)
pi.close();
}
finally {
// Call super class
super.finalize();
}
}
}

View file

@ -0,0 +1,323 @@
/*
* Copyright (c) 1994, 2014, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www;
import java.net.URL;
import java.io.*;
import java.util.StringJoiner;
import java.util.StringTokenizer;
public class MimeEntry implements Cloneable {
private String typeName; // of the form: "type/subtype"
private String tempFileNameTemplate;
private int action;
private String command;
private String description;
private String imageFileName;
private String fileExtensions[];
boolean starred;
// Actions
public static final int UNKNOWN = 0;
public static final int LOAD_INTO_BROWSER = 1;
public static final int SAVE_TO_FILE = 2;
public static final int LAUNCH_APPLICATION = 3;
static final String[] actionKeywords = {
"unknown",
"browser",
"save",
"application",
};
/**
* Construct an empty entry of the given type and subtype.
*/
public MimeEntry(String type) {
// Default action is UNKNOWN so clients can decide what the default
// should be, typically save to file or ask user.
this(type, UNKNOWN, null, null, null);
}
//
// The next two constructors are used only by the deprecated
// PlatformMimeTable classes or, in last case, is called by the public
// constructor. They are kept here anticipating putting support for
// mailcap formatted config files back in (so BOTH the properties format
// and the mailcap formats are supported).
//
MimeEntry(String type, String imageFileName, String extensionString) {
typeName = type.toLowerCase();
action = UNKNOWN;
command = null;
this.imageFileName = imageFileName;
setExtensions(extensionString);
starred = isStarred(typeName);
}
// For use with MimeTable::parseMailCap
MimeEntry(String typeName, int action, String command,
String tempFileNameTemplate) {
this.typeName = typeName.toLowerCase();
this.action = action;
this.command = command;
this.imageFileName = null;
this.fileExtensions = null;
this.tempFileNameTemplate = tempFileNameTemplate;
}
// This is the one called by the public constructor.
MimeEntry(String typeName, int action, String command,
String imageFileName, String fileExtensions[]) {
this.typeName = typeName.toLowerCase();
this.action = action;
this.command = command;
this.imageFileName = imageFileName;
this.fileExtensions = fileExtensions;
starred = isStarred(typeName);
}
public synchronized String getType() {
return typeName;
}
public synchronized void setType(String type) {
typeName = type.toLowerCase();
}
public synchronized int getAction() {
return action;
}
public synchronized void setAction(int action, String command) {
this.action = action;
this.command = command;
}
public synchronized void setAction(int action) {
this.action = action;
}
public synchronized String getLaunchString() {
return command;
}
public synchronized void setCommand(String command) {
this.command = command;
}
public synchronized String getDescription() {
return (description != null ? description : typeName);
}
public synchronized void setDescription(String description) {
this.description = description;
}
// ??? what to return for the image -- the file name or should this return
// something more advanced like an image source or something?
// returning the name has the least policy associated with it.
// pro tempore, we'll use the name
public String getImageFileName() {
return imageFileName;
}
public synchronized void setImageFileName(String filename) {
File file = new File(filename);
if (file.getParent() == null) {
imageFileName = System.getProperty(
"java.net.ftp.imagepath."+filename);
}
else {
imageFileName = filename;
}
if (filename.lastIndexOf('.') < 0) {
imageFileName = imageFileName + ".gif";
}
}
public String getTempFileTemplate() {
return tempFileNameTemplate;
}
public synchronized String[] getExtensions() {
return fileExtensions;
}
public synchronized String getExtensionsAsList() {
String extensionsAsString = "";
if (fileExtensions != null) {
for (int i = 0; i < fileExtensions.length; i++) {
extensionsAsString += fileExtensions[i];
if (i < (fileExtensions.length - 1)) {
extensionsAsString += ",";
}
}
}
return extensionsAsString;
}
public synchronized void setExtensions(String extensionString) {
StringTokenizer extTokens = new StringTokenizer(extensionString, ",");
int numExts = extTokens.countTokens();
String extensionStrings[] = new String[numExts];
for (int i = 0; i < numExts; i++) {
String ext = (String)extTokens.nextElement();
extensionStrings[i] = ext.trim();
}
fileExtensions = extensionStrings;
}
private boolean isStarred(String typeName) {
return (typeName != null)
&& (typeName.length() > 0)
&& (typeName.endsWith("/*"));
}
/**
* Invoke the MIME type specific behavior for this MIME type.
* Returned value can be one of several types:
* <ol>
* <li>A thread -- the caller can choose when to launch this thread.
* <li>A string -- the string is loaded into the browser directly.
* <li>An input stream -- the caller can read from this byte stream and
* will typically store the results in a file.
* <li>A document (?) --
* </ol>
*/
public Object launch(java.net.URLConnection urlc, InputStream is, MimeTable mt) throws ApplicationLaunchException {
switch (action) {
case SAVE_TO_FILE:
// REMIND: is this really the right thing to do?
try {
return is;
} catch(Exception e) {
// I18N
return "Load to file failed:\n" + e;
}
case LOAD_INTO_BROWSER:
// REMIND: invoke the content handler?
// may be the right thing to do, may not be -- short term
// where docs are not loaded asynch, loading and returning
// the content is the right thing to do.
try {
return urlc.getContent();
} catch (Exception e) {
return null;
}
case LAUNCH_APPLICATION:
{
String threadName = command;
int fst = threadName.indexOf(' ');
if (fst > 0) {
threadName = threadName.substring(0, fst);
}
return new MimeLauncher(this, urlc, is,
mt.getTempFileTemplate(), threadName);
}
case UNKNOWN:
// REMIND: What do do here?
return null;
}
return null;
}
public boolean matches(String type) {
if (starred) {
// REMIND: is this the right thing or not?
return type.startsWith(typeName);
} else {
return type.equals(typeName);
}
}
public Object clone() {
// return a shallow copy of this.
MimeEntry theClone = new MimeEntry(typeName);
theClone.action = action;
theClone.command = command;
theClone.description = description;
theClone.imageFileName = imageFileName;
theClone.tempFileNameTemplate = tempFileNameTemplate;
theClone.fileExtensions = fileExtensions;
return theClone;
}
public synchronized String toProperty() {
StringJoiner sj = new StringJoiner("; ");
int action = getAction();
if (action != MimeEntry.UNKNOWN) {
sj.add("action=" + actionKeywords[action]);
}
String command = getLaunchString();
if (command != null && command.length() > 0) {
sj.add("application=" + command);
}
String image = getImageFileName();
if (image != null) {
sj.add("icon=" + image);
}
String extensions = getExtensionsAsList();
if (extensions.length() > 0) {
sj.add("file_extensions=" + extensions);
}
String description = getDescription();
if (description != null && !description.equals(getType())) {
sj.add("description=" + description);
}
return sj.toString();
}
public String toString() {
return "MimeEntry[contentType=" + typeName
+ ", image=" + imageFileName
+ ", action=" + action
+ ", command=" + command
+ ", extensions=" + getExtensionsAsList()
+ "]";
}
}

View file

@ -0,0 +1,205 @@
/*
* Copyright (c) 1994, 1998, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www;
import java.net.URL;
import java.io.*;
import java.util.StringTokenizer;
import sun.security.action.GetPropertyAction;
class MimeLauncher extends Thread {
java.net.URLConnection uc;
MimeEntry m;
String genericTempFileTemplate;
InputStream is;
String execPath;
MimeLauncher (MimeEntry M, java.net.URLConnection uc,
InputStream is, String tempFileTemplate, String threadName) throws ApplicationLaunchException {
super(null, null, threadName, 0, false);
m = M;
this.uc = uc;
this.is = is;
genericTempFileTemplate = tempFileTemplate;
/* get the application to launch */
String launchString = m.getLaunchString();
/* get a valid path to launch application - sets
the execPath instance variable with the correct path.
*/
if (!findExecutablePath(launchString)) {
/* strip off parameters i.e %s */
String appName;
int index = launchString.indexOf(' ');
if (index != -1) {
appName = launchString.substring(0, index);
}
else {
appName = launchString;
}
throw new ApplicationLaunchException(appName);
}
}
protected String getTempFileName(URL url, String template) {
String tempFilename = template;
// Replace all but last occurrance of "%s" with timestamp to insure
// uniqueness. There's a subtle behavior here: if there is anything
// _after_ the last "%s" we need to append it so that unusual launch
// strings that have the datafile in the middle can still be used.
int wildcard = tempFilename.lastIndexOf("%s");
String prefix = tempFilename.substring(0, wildcard);
String suffix = "";
if (wildcard < tempFilename.length() - 2) {
suffix = tempFilename.substring(wildcard + 2);
}
long timestamp = System.currentTimeMillis()/1000;
int argIndex = 0;
while ((argIndex = prefix.indexOf("%s")) >= 0) {
prefix = prefix.substring(0, argIndex)
+ timestamp
+ prefix.substring(argIndex + 2);
}
// Add a file name and file-extension if known
String filename = url.getFile();
String extension = "";
int dot = filename.lastIndexOf('.');
// BugId 4084826: Temp MIME file names not always valid.
// Fix: don't allow slashes in the file name or extension.
if (dot >= 0 && dot > filename.lastIndexOf('/')) {
extension = filename.substring(dot);
}
filename = "HJ" + url.hashCode();
tempFilename = prefix + filename + timestamp + extension + suffix;
return tempFilename;
}
public void run() {
try {
String ofn = m.getTempFileTemplate();
if (ofn == null) {
ofn = genericTempFileTemplate;
}
ofn = getTempFileName(uc.getURL(), ofn);
try {
OutputStream os = new FileOutputStream(ofn);
byte buf[] = new byte[2048];
int i = 0;
try {
while ((i = is.read(buf)) >= 0) {
os.write(buf, 0, i);
}
} catch(IOException e) {
//System.err.println("Exception in write loop " + i);
//e.printStackTrace();
} finally {
os.close();
is.close();
}
} catch(IOException e) {
//System.err.println("Exception in input or output stream");
//e.printStackTrace();
}
int inx = 0;
String c = execPath;
while ((inx = c.indexOf("%t")) >= 0) {
c = c.substring(0, inx) + uc.getContentType()
+ c.substring(inx + 2);
}
boolean substituted = false;
while ((inx = c.indexOf("%s")) >= 0) {
c = c.substring(0, inx) + ofn + c.substring(inx + 2);
substituted = true;
}
if (!substituted)
c = c + " <" + ofn;
// System.out.println("Execing " +c);
Runtime.getRuntime().exec(c);
} catch(IOException e) {
}
}
/* This method determines the path for the launcher application
and sets the execPath instance variable. It uses the exec.path
property to obtain a list of paths that is in turn used to
location the application. If a valid path is not found, it
returns false else true. */
private boolean findExecutablePath(String str) {
if (str == null || str.length() == 0) {
return false;
}
String command;
int index = str.indexOf(' ');
if (index != -1) {
command = str.substring(0, index);
}
else {
command = str;
}
File f = new File(command);
if (f.isFile()) {
// Already executable as it is
execPath = str;
return true;
}
String execPathList;
execPathList = GetPropertyAction.privilegedGetProperty("exec.path");
if (execPathList == null) {
// exec.path property not set
return false;
}
StringTokenizer iter = new StringTokenizer(execPathList, "|");
while (iter.hasMoreElements()) {
String prefix = (String)iter.nextElement();
String fullCmd = prefix + File.separator + command;
f = new File(fullCmd);
if (f.isFile()) {
execPath = prefix + File.separator + str;
return true;
}
}
return false; // application not found in exec.path
}
}

View file

@ -0,0 +1,436 @@
/*
* Copyright (c) 1994, 2011, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www;
import java.io.*;
import java.net.FileNameMap;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Properties;
import java.util.StringTokenizer;
public class MimeTable implements FileNameMap {
/** Keyed by content type, returns MimeEntries */
private Hashtable<String, MimeEntry> entries
= new Hashtable<String, MimeEntry>();
/** Keyed by file extension (with the .), returns MimeEntries */
private Hashtable<String, MimeEntry> extensionMap
= new Hashtable<String, MimeEntry>();
// Will be reset if in the platform-specific data file
private static String tempFileTemplate;
static {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
tempFileTemplate =
System.getProperty("content.types.temp.file.template",
"/tmp/%s");
mailcapLocations = new String[] {
System.getProperty("user.mailcap"),
System.getProperty("user.home") + "/.mailcap",
"/etc/mailcap",
"/usr/etc/mailcap",
"/usr/local/etc/mailcap",
System.getProperty("hotjava.home",
"/usr/local/hotjava")
+ "/lib/mailcap",
};
return null;
}
});
}
private static final String filePreamble = "sun.net.www MIME content-types table";
private static final String fileMagic = "#" + filePreamble;
MimeTable() {
load();
}
private static class DefaultInstanceHolder {
static final MimeTable defaultInstance = getDefaultInstance();
static MimeTable getDefaultInstance() {
return java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<MimeTable>() {
public MimeTable run() {
MimeTable instance = new MimeTable();
URLConnection.setFileNameMap(instance);
return instance;
}
});
}
}
/**
* Get the single instance of this class. First use will load the
* table from a data file.
*/
public static MimeTable getDefaultTable() {
return DefaultInstanceHolder.defaultInstance;
}
/**
*
*/
public static FileNameMap loadTable() {
MimeTable mt = getDefaultTable();
return (FileNameMap)mt;
}
public synchronized int getSize() {
return entries.size();
}
public synchronized String getContentTypeFor(String fileName) {
MimeEntry entry = findByFileName(fileName);
if (entry != null) {
return entry.getType();
} else {
return null;
}
}
public synchronized void add(MimeEntry m) {
entries.put(m.getType(), m);
String exts[] = m.getExtensions();
if (exts == null) {
return;
}
for (int i = 0; i < exts.length; i++) {
extensionMap.put(exts[i], m);
}
}
public synchronized MimeEntry remove(String type) {
MimeEntry entry = entries.get(type);
return remove(entry);
}
public synchronized MimeEntry remove(MimeEntry entry) {
String[] extensionKeys = entry.getExtensions();
if (extensionKeys != null) {
for (int i = 0; i < extensionKeys.length; i++) {
extensionMap.remove(extensionKeys[i]);
}
}
return entries.remove(entry.getType());
}
public synchronized MimeEntry find(String type) {
MimeEntry entry = entries.get(type);
if (entry == null) {
// try a wildcard lookup
Enumeration<MimeEntry> e = entries.elements();
while (e.hasMoreElements()) {
MimeEntry wild = e.nextElement();
if (wild.matches(type)) {
return wild;
}
}
}
return entry;
}
/**
* Locate a MimeEntry by the file extension that has been associated
* with it. Parses general file names, and URLs.
*/
public MimeEntry findByFileName(String fname) {
String ext = "";
int i = fname.lastIndexOf('#');
if (i > 0) {
fname = fname.substring(0, i - 1);
}
i = fname.lastIndexOf('.');
// REMIND: OS specific delimters appear here
i = Math.max(i, fname.lastIndexOf('/'));
i = Math.max(i, fname.lastIndexOf('?'));
if (i != -1 && fname.charAt(i) == '.') {
ext = fname.substring(i).toLowerCase();
}
return findByExt(ext);
}
/**
* Locate a MimeEntry by the file extension that has been associated
* with it.
*/
public synchronized MimeEntry findByExt(String fileExtension) {
return extensionMap.get(fileExtension);
}
public synchronized MimeEntry findByDescription(String description) {
Enumeration<MimeEntry> e = elements();
while (e.hasMoreElements()) {
MimeEntry entry = e.nextElement();
if (description.equals(entry.getDescription())) {
return entry;
}
}
// We failed, now try treating description as type
return find(description);
}
String getTempFileTemplate() {
return tempFileTemplate;
}
public synchronized Enumeration<MimeEntry> elements() {
return entries.elements();
}
// For backward compatibility -- mailcap format files
// This is not currently used, but may in the future when we add ability
// to read BOTH the properties format and the mailcap format.
protected static String[] mailcapLocations;
public synchronized void load() {
Properties entries = new Properties();
File file = null;
InputStream in;
// First try to load the user-specific table, if it exists
String userTablePath = System.getProperty("content.types.user.table");
if (userTablePath != null && (file = new File(userTablePath)).exists()) {
try {
in = new FileInputStream(file);
} catch (FileNotFoundException e) {
System.err.println("Warning: " + file.getPath()
+ " mime table not found.");
return;
}
} else {
in = MimeTable.class.getResourceAsStream("content-types.properties");
if (in == null)
throw new InternalError("default mime table not found");
}
try (BufferedInputStream bin = new BufferedInputStream(in)) {
entries.load(bin);
} catch (IOException e) {
System.err.println("Warning: " + e.getMessage());
}
parse(entries);
}
void parse(Properties entries) {
// first, strip out the platform-specific temp file template
String tempFileTemplate = (String)entries.get("temp.file.template");
if (tempFileTemplate != null) {
entries.remove("temp.file.template");
MimeTable.tempFileTemplate = tempFileTemplate;
}
// now, parse the mime-type spec's
Enumeration<?> types = entries.propertyNames();
while (types.hasMoreElements()) {
String type = (String)types.nextElement();
String attrs = entries.getProperty(type);
parse(type, attrs);
}
}
//
// Table format:
//
// <entry> ::= <table_tag> | <type_entry>
//
// <table_tag> ::= <table_format_version> | <temp_file_template>
//
// <type_entry> ::= <type_subtype_pair> '=' <type_attrs_list>
//
// <type_subtype_pair> ::= <type> '/' <subtype>
//
// <type_attrs_list> ::= <attr_value_pair> [ ';' <attr_value_pair> ]*
// | [ <attr_value_pair> ]+
//
// <attr_value_pair> ::= <attr_name> '=' <attr_value>
//
// <attr_name> ::= 'description' | 'action' | 'application'
// | 'file_extensions' | 'icon'
//
// <attr_value> ::= <legal_char>*
//
// Embedded ';' in an <attr_value> are quoted with leading '\' .
//
// Interpretation of <attr_value> depends on the <attr_name> it is
// associated with.
//
void parse(String type, String attrs) {
MimeEntry newEntry = new MimeEntry(type);
// REMIND handle embedded ';' and '|' and literal '"'
StringTokenizer tokenizer = new StringTokenizer(attrs, ";");
while (tokenizer.hasMoreTokens()) {
String pair = tokenizer.nextToken();
parse(pair, newEntry);
}
add(newEntry);
}
void parse(String pair, MimeEntry entry) {
// REMIND add exception handling...
String name = null;
String value = null;
boolean gotName = false;
StringTokenizer tokenizer = new StringTokenizer(pair, "=");
while (tokenizer.hasMoreTokens()) {
if (gotName) {
value = tokenizer.nextToken().trim();
}
else {
name = tokenizer.nextToken().trim();
gotName = true;
}
}
fill(entry, name, value);
}
void fill(MimeEntry entry, String name, String value) {
if ("description".equalsIgnoreCase(name)) {
entry.setDescription(value);
}
else if ("action".equalsIgnoreCase(name)) {
entry.setAction(getActionCode(value));
}
else if ("application".equalsIgnoreCase(name)) {
entry.setCommand(value);
}
else if ("icon".equalsIgnoreCase(name)) {
entry.setImageFileName(value);
}
else if ("file_extensions".equalsIgnoreCase(name)) {
entry.setExtensions(value);
}
// else illegal name exception
}
String[] getExtensions(String list) {
StringTokenizer tokenizer = new StringTokenizer(list, ",");
int n = tokenizer.countTokens();
String[] extensions = new String[n];
for (int i = 0; i < n; i++) {
extensions[i] = tokenizer.nextToken();
}
return extensions;
}
int getActionCode(String action) {
for (int i = 0; i < MimeEntry.actionKeywords.length; i++) {
if (action.equalsIgnoreCase(MimeEntry.actionKeywords[i])) {
return i;
}
}
return MimeEntry.UNKNOWN;
}
public Properties getAsProperties() {
Properties properties = new Properties();
Enumeration<MimeEntry> e = elements();
while (e.hasMoreElements()) {
MimeEntry entry = e.nextElement();
properties.put(entry.getType(), entry.toProperty());
}
return properties;
}
protected boolean saveAsProperties(File file) {
FileOutputStream os = null;
try {
os = new FileOutputStream(file);
Properties properties = getAsProperties();
properties.put("temp.file.template", tempFileTemplate);
String tag;
String user = System.getProperty("user.name");
if (user != null) {
tag = "; customized for " + user;
properties.store(os, filePreamble + tag);
}
else {
properties.store(os, filePreamble);
}
}
catch (IOException e) {
e.printStackTrace();
return false;
}
finally {
if (os != null) {
try { os.close(); } catch (IOException e) {}
}
}
return true;
}
/*
* Debugging utilities
*
public void list(PrintStream out) {
Enumeration keys = entries.keys();
while (keys.hasMoreElements()) {
String key = (String)keys.nextElement();
MimeEntry entry = (MimeEntry)entries.get(key);
out.println(key + ": " + entry);
}
}
public static void main(String[] args) {
MimeTable testTable = MimeTable.getDefaultTable();
Enumeration e = testTable.elements();
while (e.hasMoreElements()) {
MimeEntry entry = (MimeEntry)e.nextElement();
System.out.println(entry);
}
testTable.save(File.separator + "tmp" +
File.separator + "mime_table.save");
}
*/
}

View file

@ -0,0 +1,696 @@
/*
* Copyright (c) 1998, 2007, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www;
import java.io.File;
import java.net.URL;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import sun.nio.cs.ThreadLocalCoders;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
/**
* A class that contains useful routines common to sun.net.www
* @author Mike McCloskey
*/
public class ParseUtil {
/**
* Constructs an encoded version of the specified path string suitable
* for use in the construction of a URL.
*
* A path separator is replaced by a forward slash. The string is UTF8
* encoded. The % escape sequence is used for characters that are above
* 0x7F or those defined in RFC2396 as reserved or excluded in the path
* component of a URL.
*/
public static String encodePath(String path) {
return encodePath(path, true);
}
/*
* flag indicates whether path uses platform dependent
* File.separatorChar or not. True indicates path uses platform
* dependent File.separatorChar.
*/
public static String encodePath(String path, boolean flag) {
if (flag && File.separatorChar != '/') {
return encodePath(path, 0, File.separatorChar);
} else {
int index = firstEncodeIndex(path);
if (index > -1) {
return encodePath(path, index, '/');
} else {
return path;
}
}
}
private static int firstEncodeIndex(String path) {
int len = path.length();
for (int i = 0; i < len; i++) {
char c = path.charAt(i);
if (c == '/' || c == '.' ||
c >= 'a' && c <= 'z' ||
c >= 'A' && c <= 'Z' ||
c >= '0' && c <= '9') {
continue;
} else if (c > 0x007F || match(c, L_ENCODED, H_ENCODED)) {
return i;
}
}
return -1;
}
private static String encodePath(String path, int index, char sep) {
char[] pathCC = path.toCharArray();
char[] retCC = new char[pathCC.length * 2 + 16 - index];
if (index > 0) {
System.arraycopy(pathCC, 0, retCC, 0, index);
}
int retLen = index;
for (int i = index; i < pathCC.length; i++) {
char c = pathCC[i];
if (c == sep)
retCC[retLen++] = '/';
else {
if (c <= 0x007F) {
if (c >= 'a' && c <= 'z' ||
c >= 'A' && c <= 'Z' ||
c >= '0' && c <= '9') {
retCC[retLen++] = c;
} else if (match(c, L_ENCODED, H_ENCODED)) {
retLen = escape(retCC, c, retLen);
} else {
retCC[retLen++] = c;
}
} else if (c > 0x07FF) {
retLen = escape(retCC, (char)(0xE0 | ((c >> 12) & 0x0F)), retLen);
retLen = escape(retCC, (char)(0x80 | ((c >> 6) & 0x3F)), retLen);
retLen = escape(retCC, (char)(0x80 | ((c >> 0) & 0x3F)), retLen);
} else {
retLen = escape(retCC, (char)(0xC0 | ((c >> 6) & 0x1F)), retLen);
retLen = escape(retCC, (char)(0x80 | ((c >> 0) & 0x3F)), retLen);
}
}
//worst case scenario for character [0x7ff-] every single
//character will be encoded into 9 characters.
if (retLen + 9 > retCC.length) {
int newLen = retCC.length * 2 + 16;
if (newLen < 0) {
newLen = Integer.MAX_VALUE;
}
char[] buf = new char[newLen];
System.arraycopy(retCC, 0, buf, 0, retLen);
retCC = buf;
}
}
return new String(retCC, 0, retLen);
}
/**
* Appends the URL escape sequence for the specified char to the
* specified StringBuffer.
*/
private static int escape(char[] cc, char c, int index) {
cc[index++] = '%';
cc[index++] = Character.forDigit((c >> 4) & 0xF, 16);
cc[index++] = Character.forDigit(c & 0xF, 16);
return index;
}
/**
* Un-escape and return the character at position i in string s.
*/
private static byte unescape(String s, int i) {
return (byte) Integer.parseInt(s, i + 1, i + 3, 16);
}
/**
* Returns a new String constructed from the specified String by replacing
* the URL escape sequences and UTF8 encoding with the characters they
* represent.
*/
public static String decode(String s) {
int n = s.length();
if ((n == 0) || (s.indexOf('%') < 0))
return s;
StringBuilder sb = new StringBuilder(n);
ByteBuffer bb = ByteBuffer.allocate(n);
CharBuffer cb = CharBuffer.allocate(n);
CharsetDecoder dec = ThreadLocalCoders.decoderFor("UTF-8")
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
char c = s.charAt(0);
for (int i = 0; i < n;) {
assert c == s.charAt(i);
if (c != '%') {
sb.append(c);
if (++i >= n)
break;
c = s.charAt(i);
continue;
}
bb.clear();
int ui = i;
for (;;) {
assert (n - i >= 2);
try {
bb.put(unescape(s, i));
} catch (NumberFormatException e) {
throw new IllegalArgumentException();
}
i += 3;
if (i >= n)
break;
c = s.charAt(i);
if (c != '%')
break;
}
bb.flip();
cb.clear();
dec.reset();
CoderResult cr = dec.decode(bb, cb, true);
if (cr.isError())
throw new IllegalArgumentException("Error decoding percent encoded characters");
cr = dec.flush(cb);
if (cr.isError())
throw new IllegalArgumentException("Error decoding percent encoded characters");
sb.append(cb.flip().toString());
}
return sb.toString();
}
/**
* Returns a canonical version of the specified string.
*/
public String canonizeString(String file) {
int i = 0;
int lim = file.length();
// Remove embedded /../
while ((i = file.indexOf("/../")) >= 0) {
if ((lim = file.lastIndexOf('/', i - 1)) >= 0) {
file = file.substring(0, lim) + file.substring(i + 3);
} else {
file = file.substring(i + 3);
}
}
// Remove embedded /./
while ((i = file.indexOf("/./")) >= 0) {
file = file.substring(0, i) + file.substring(i + 2);
}
// Remove trailing ..
while (file.endsWith("/..")) {
i = file.indexOf("/..");
if ((lim = file.lastIndexOf('/', i - 1)) >= 0) {
file = file.substring(0, lim+1);
} else {
file = file.substring(0, i);
}
}
// Remove trailing .
if (file.endsWith("/."))
file = file.substring(0, file.length() -1);
return file;
}
public static URL fileToEncodedURL(File file)
throws MalformedURLException
{
String path = file.getAbsolutePath();
path = ParseUtil.encodePath(path);
if (!path.startsWith("/")) {
path = "/" + path;
}
if (!path.endsWith("/") && file.isDirectory()) {
path = path + "/";
}
return new URL("file", "", path);
}
public static java.net.URI toURI(URL url) {
String protocol = url.getProtocol();
String auth = url.getAuthority();
String path = url.getPath();
String query = url.getQuery();
String ref = url.getRef();
if (path != null && !(path.startsWith("/")))
path = "/" + path;
//
// In java.net.URI class, a port number of -1 implies the default
// port number. So get it stripped off before creating URI instance.
//
if (auth != null && auth.endsWith(":-1"))
auth = auth.substring(0, auth.length() - 3);
java.net.URI uri;
try {
uri = createURI(protocol, auth, path, query, ref);
} catch (java.net.URISyntaxException e) {
uri = null;
}
return uri;
}
//
// createURI() and its auxiliary code are cloned from java.net.URI.
// Most of the code are just copy and paste, except that quote()
// has been modified to avoid double-escape.
//
// Usually it is unacceptable, but we're forced to do it because
// otherwise we need to change public API, namely java.net.URI's
// multi-argument constructors. It turns out that the changes cause
// incompatibilities so can't be done.
//
private static URI createURI(String scheme,
String authority,
String path,
String query,
String fragment) throws URISyntaxException
{
String s = toString(scheme, null,
authority, null, null, -1,
path, query, fragment);
checkPath(s, scheme, path);
return new URI(s);
}
private static String toString(String scheme,
String opaquePart,
String authority,
String userInfo,
String host,
int port,
String path,
String query,
String fragment)
{
StringBuffer sb = new StringBuffer();
if (scheme != null) {
sb.append(scheme);
sb.append(':');
}
appendSchemeSpecificPart(sb, opaquePart,
authority, userInfo, host, port,
path, query);
appendFragment(sb, fragment);
return sb.toString();
}
private static void appendSchemeSpecificPart(StringBuffer sb,
String opaquePart,
String authority,
String userInfo,
String host,
int port,
String path,
String query)
{
if (opaquePart != null) {
/* check if SSP begins with an IPv6 address
* because we must not quote a literal IPv6 address
*/
if (opaquePart.startsWith("//[")) {
int end = opaquePart.indexOf(']');
if (end != -1 && opaquePart.indexOf(':')!=-1) {
String doquote, dontquote;
if (end == opaquePart.length()) {
dontquote = opaquePart;
doquote = "";
} else {
dontquote = opaquePart.substring(0,end+1);
doquote = opaquePart.substring(end+1);
}
sb.append (dontquote);
sb.append(quote(doquote, L_URIC, H_URIC));
}
} else {
sb.append(quote(opaquePart, L_URIC, H_URIC));
}
} else {
appendAuthority(sb, authority, userInfo, host, port);
if (path != null)
sb.append(quote(path, L_PATH, H_PATH));
if (query != null) {
sb.append('?');
sb.append(quote(query, L_URIC, H_URIC));
}
}
}
private static void appendAuthority(StringBuffer sb,
String authority,
String userInfo,
String host,
int port)
{
if (host != null) {
sb.append("//");
if (userInfo != null) {
sb.append(quote(userInfo, L_USERINFO, H_USERINFO));
sb.append('@');
}
boolean needBrackets = ((host.indexOf(':') >= 0)
&& !host.startsWith("[")
&& !host.endsWith("]"));
if (needBrackets) sb.append('[');
sb.append(host);
if (needBrackets) sb.append(']');
if (port != -1) {
sb.append(':');
sb.append(port);
}
} else if (authority != null) {
sb.append("//");
if (authority.startsWith("[")) {
int end = authority.indexOf(']');
if (end != -1 && authority.indexOf(':')!=-1) {
String doquote, dontquote;
if (end == authority.length()) {
dontquote = authority;
doquote = "";
} else {
dontquote = authority.substring(0,end+1);
doquote = authority.substring(end+1);
}
sb.append (dontquote);
sb.append(quote(doquote,
L_REG_NAME | L_SERVER,
H_REG_NAME | H_SERVER));
}
} else {
sb.append(quote(authority,
L_REG_NAME | L_SERVER,
H_REG_NAME | H_SERVER));
}
}
}
private static void appendFragment(StringBuffer sb, String fragment) {
if (fragment != null) {
sb.append('#');
sb.append(quote(fragment, L_URIC, H_URIC));
}
}
// Quote any characters in s that are not permitted
// by the given mask pair
//
private static String quote(String s, long lowMask, long highMask) {
int n = s.length();
StringBuffer sb = null;
boolean allowNonASCII = ((lowMask & L_ESCAPED) != 0);
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c < '\u0080') {
if (!match(c, lowMask, highMask) && !isEscaped(s, i)) {
if (sb == null) {
sb = new StringBuffer();
sb.append(s, 0, i);
}
appendEscape(sb, (byte)c);
} else {
if (sb != null)
sb.append(c);
}
} else if (allowNonASCII
&& (Character.isSpaceChar(c)
|| Character.isISOControl(c))) {
if (sb == null) {
sb = new StringBuffer();
sb.append(s, 0, i);
}
appendEncoded(sb, c);
} else {
if (sb != null)
sb.append(c);
}
}
return (sb == null) ? s : sb.toString();
}
//
// To check if the given string has an escaped triplet
// at the given position
//
private static boolean isEscaped(String s, int pos) {
if (s == null || (s.length() <= (pos + 2)))
return false;
return s.charAt(pos) == '%'
&& match(s.charAt(pos + 1), L_HEX, H_HEX)
&& match(s.charAt(pos + 2), L_HEX, H_HEX);
}
private static void appendEncoded(StringBuffer sb, char c) {
ByteBuffer bb = null;
try {
bb = ThreadLocalCoders.encoderFor("UTF-8")
.encode(CharBuffer.wrap("" + c));
} catch (CharacterCodingException x) {
assert false;
}
while (bb.hasRemaining()) {
int b = bb.get() & 0xff;
if (b >= 0x80)
appendEscape(sb, (byte)b);
else
sb.append((char)b);
}
}
private static final char[] hexDigits = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
private static void appendEscape(StringBuffer sb, byte b) {
sb.append('%');
sb.append(hexDigits[(b >> 4) & 0x0f]);
sb.append(hexDigits[(b >> 0) & 0x0f]);
}
// Tell whether the given character is permitted by the given mask pair
private static boolean match(char c, long lowMask, long highMask) {
if (c < 64)
return ((1L << c) & lowMask) != 0;
if (c < 128)
return ((1L << (c - 64)) & highMask) != 0;
return false;
}
// If a scheme is given then the path, if given, must be absolute
//
private static void checkPath(String s, String scheme, String path)
throws URISyntaxException
{
if (scheme != null) {
if ((path != null)
&& ((path.length() > 0) && (path.charAt(0) != '/')))
throw new URISyntaxException(s,
"Relative path in absolute URI");
}
}
// -- Character classes for parsing --
// To save startup time, we manually calculate the low-/highMask constants.
// For reference, the following methods were used to calculate the values:
// Compute a low-order mask for the characters
// between first and last, inclusive
// private static long lowMask(char first, char last) {
// long m = 0;
// int f = Math.max(Math.min(first, 63), 0);
// int l = Math.max(Math.min(last, 63), 0);
// for (int i = f; i <= l; i++)
// m |= 1L << i;
// return m;
// }
// Compute the low-order mask for the characters in the given string
// private static long lowMask(String chars) {
// int n = chars.length();
// long m = 0;
// for (int i = 0; i < n; i++) {
// char c = chars.charAt(i);
// if (c < 64)
// m |= (1L << c);
// }
// return m;
// }
// Compute a high-order mask for the characters
// between first and last, inclusive
// private static long highMask(char first, char last) {
// long m = 0;
// int f = Math.max(Math.min(first, 127), 64) - 64;
// int l = Math.max(Math.min(last, 127), 64) - 64;
// for (int i = f; i <= l; i++)
// m |= 1L << i;
// return m;
// }
// Compute the high-order mask for the characters in the given string
// private static long highMask(String chars) {
// int n = chars.length();
// long m = 0;
// for (int i = 0; i < n; i++) {
// char c = chars.charAt(i);
// if ((c >= 64) && (c < 128))
// m |= (1L << (c - 64));
// }
// return m;
// }
// Character-class masks
// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
// "8" | "9"
private static final long L_DIGIT = 0x3FF000000000000L; // lowMask('0', '9');
private static final long H_DIGIT = 0L;
// hex = digit | "A" | "B" | "C" | "D" | "E" | "F" |
// "a" | "b" | "c" | "d" | "e" | "f"
private static final long L_HEX = L_DIGIT;
private static final long H_HEX = 0x7E0000007EL; // highMask('A', 'F') | highMask('a', 'f');
// upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
// "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
// "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
private static final long L_UPALPHA = 0L;
private static final long H_UPALPHA = 0x7FFFFFEL; // highMask('A', 'Z');
// lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" |
// "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" |
// "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
private static final long L_LOWALPHA = 0L;
private static final long H_LOWALPHA = 0x7FFFFFE00000000L; // highMask('a', 'z');
// alpha = lowalpha | upalpha
private static final long L_ALPHA = L_LOWALPHA | L_UPALPHA;
private static final long H_ALPHA = H_LOWALPHA | H_UPALPHA;
// alphanum = alpha | digit
private static final long L_ALPHANUM = L_DIGIT | L_ALPHA;
private static final long H_ALPHANUM = H_DIGIT | H_ALPHA;
// mark = "-" | "_" | "." | "!" | "~" | "*" | "'" |
// "(" | ")"
private static final long L_MARK = 0x678200000000L; // lowMask("-_.!~*'()");
private static final long H_MARK = 0x4000000080000000L; // highMask("-_.!~*'()");
// unreserved = alphanum | mark
private static final long L_UNRESERVED = L_ALPHANUM | L_MARK;
private static final long H_UNRESERVED = H_ALPHANUM | H_MARK;
// reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
// "$" | "," | "[" | "]"
// Added per RFC2732: "[", "]"
private static final long L_RESERVED = 0xAC00985000000000L; // lowMask(";/?:@&=+$,[]");
private static final long H_RESERVED = 0x28000001L; // highMask(";/?:@&=+$,[]");
// The zero'th bit is used to indicate that escape pairs and non-US-ASCII
// characters are allowed; this is handled by the scanEscape method below.
private static final long L_ESCAPED = 1L;
private static final long H_ESCAPED = 0L;
// uric = reserved | unreserved | escaped
private static final long L_URIC = L_RESERVED | L_UNRESERVED | L_ESCAPED;
private static final long H_URIC = H_RESERVED | H_UNRESERVED | H_ESCAPED;
// pchar = unreserved | escaped |
// ":" | "@" | "&" | "=" | "+" | "$" | ","
private static final long L_PCHAR
= L_UNRESERVED | L_ESCAPED | 0x2400185000000000L; // lowMask(":@&=+$,");
private static final long H_PCHAR
= H_UNRESERVED | H_ESCAPED | 0x1L; // highMask(":@&=+$,");
// All valid path characters
private static final long L_PATH = L_PCHAR | 0x800800000000000L; // lowMask(";/");
private static final long H_PATH = H_PCHAR; // highMask(";/") == 0x0L;
// Dash, for use in domainlabel and toplabel
private static final long L_DASH = 0x200000000000L; // lowMask("-");
private static final long H_DASH = 0x0L; // highMask("-");
// userinfo = *( unreserved | escaped |
// ";" | ":" | "&" | "=" | "+" | "$" | "," )
private static final long L_USERINFO
= L_UNRESERVED | L_ESCAPED | 0x2C00185000000000L; // lowMask(";:&=+$,");
private static final long H_USERINFO
= H_UNRESERVED | H_ESCAPED; // | highMask(";:&=+$,") == 0L;
// reg_name = 1*( unreserved | escaped | "$" | "," |
// ";" | ":" | "@" | "&" | "=" | "+" )
private static final long L_REG_NAME
= L_UNRESERVED | L_ESCAPED | 0x2C00185000000000L; // lowMask("$,;:@&=+");
private static final long H_REG_NAME
= H_UNRESERVED | H_ESCAPED | 0x1L; // highMask("$,;:@&=+");
// All valid characters for server-based authorities
private static final long L_SERVER
= L_USERINFO | L_ALPHANUM | L_DASH | 0x400400000000000L; // lowMask(".:@[]");
private static final long H_SERVER
= H_USERINFO | H_ALPHANUM | H_DASH | 0x28000001L; // highMask(".:@[]");
// Characters that are encoded in the path component of a URI.
//
// These characters are reserved in the path segment as described in
// RFC2396 section 3.3:
// "=" | ";" | "?" | "/"
//
// These characters are defined as excluded in RFC2396 section 2.4.3
// and must be escaped if they occur in the data part of a URI:
// "#" | " " | "<" | ">" | "%" | "\"" | "{" | "}" | "|" | "\\" | "^" |
// "[" | "]" | "`"
//
// Also US ASCII control characters 00-1F and 7F.
// lowMask((char)0, (char)31) | lowMask("=;?/# <>%\"{}|\\^[]`");
private static final long L_ENCODED = 0xF800802DFFFFFFFFL;
// highMask((char)0x7F, (char)0x7F) | highMask("=;?/# <>%\"{}|\\^[]`");
private static final long H_ENCODED = 0xB800000178000000L;
}

View file

@ -0,0 +1,246 @@
/*
* Copyright (c) 1995, 2011, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www;
import java.net.URL;
import java.util.*;
/**
* A class to represent an active connection to an object
* represented by a URL.
* @author James Gosling
*/
public abstract class URLConnection extends java.net.URLConnection {
/** The URL that it is connected to */
private String contentType;
private int contentLength = -1;
protected MessageHeader properties;
/** Create a URLConnection object. These should not be created directly:
instead they should be created by protocol handers in response to
URL.openConnection.
@param u The URL that this connects to.
*/
public URLConnection (URL u) {
super(u);
properties = new MessageHeader();
}
/** Call this routine to get the property list for this object.
* Properties (like content-type) that have explicit getXX() methods
* associated with them should be accessed using those methods. */
public MessageHeader getProperties() {
return properties;
}
/** Call this routine to set the property list for this object. */
public void setProperties(MessageHeader properties) {
this.properties = properties;
}
public void setRequestProperty(String key, String value) {
if(connected)
throw new IllegalAccessError("Already connected");
if (key == null)
throw new NullPointerException ("key cannot be null");
properties.set(key, value);
}
/**
* The following three methods addRequestProperty, getRequestProperty,
* and getRequestProperties were copied from the superclass implementation
* before it was changed by CR:6230836, to maintain backward compatibility.
*/
public void addRequestProperty(String key, String value) {
if (connected)
throw new IllegalStateException("Already connected");
if (key == null)
throw new NullPointerException ("key is null");
}
public String getRequestProperty(String key) {
if (connected)
throw new IllegalStateException("Already connected");
return null;
}
public Map<String,List<String>> getRequestProperties() {
if (connected)
throw new IllegalStateException("Already connected");
return Collections.emptyMap();
}
public String getHeaderField(String name) {
try {
getInputStream();
} catch (Exception e) {
return null;
}
return properties == null ? null : properties.findValue(name);
}
/**
* Return the key for the nth header field. Returns null if
* there are fewer than n fields. This can be used to iterate
* through all the headers in the message.
*/
public String getHeaderFieldKey(int n) {
try {
getInputStream();
} catch (Exception e) {
return null;
}
MessageHeader props = properties;
return props == null ? null : props.getKey(n);
}
/**
* Return the value for the nth header field. Returns null if
* there are fewer than n fields. This can be used in conjunction
* with getHeaderFieldKey to iterate through all the headers in the message.
*/
public String getHeaderField(int n) {
try {
getInputStream();
} catch (Exception e) {
return null;
}
MessageHeader props = properties;
return props == null ? null : props.getValue(n);
}
/** Call this routine to get the content-type associated with this
* object.
*/
public String getContentType() {
if (contentType == null)
contentType = getHeaderField("content-type");
if (contentType == null) {
String ct = null;
try {
ct = guessContentTypeFromStream(getInputStream());
} catch(java.io.IOException e) {
}
String ce = properties.findValue("content-encoding");
if (ct == null) {
ct = properties.findValue("content-type");
if (ct == null)
if (url.getFile().endsWith("/"))
ct = "text/html";
else
ct = guessContentTypeFromName(url.getFile());
}
/*
* If the Mime header had a Content-encoding field and its value
* was not one of the values that essentially indicate no
* encoding, we force the content type to be unknown. This will
* cause a save dialog to be presented to the user. It is not
* ideal but is better than what we were previously doing, namely
* bringing up an image tool for compressed tar files.
*/
if (ct == null || ce != null &&
!(ce.equalsIgnoreCase("7bit")
|| ce.equalsIgnoreCase("8bit")
|| ce.equalsIgnoreCase("binary")))
ct = "content/unknown";
setContentType(ct);
}
return contentType;
}
/**
* Set the content type of this URL to a specific value.
* @param type The content type to use. One of the
* content_* static variables in this
* class should be used.
* eg. setType(URL.content_html);
*/
public void setContentType(String type) {
contentType = type;
properties.set("content-type", type);
}
/** Call this routine to get the content-length associated with this
* object.
*/
public int getContentLength() {
try {
getInputStream();
} catch (Exception e) {
return -1;
}
int l = contentLength;
if (l < 0) {
try {
l = Integer.parseInt(properties.findValue("content-length"));
setContentLength(l);
} catch(Exception e) {
}
}
return l;
}
/** Call this routine to set the content-length associated with this
* object.
*/
protected void setContentLength(int length) {
contentLength = length;
properties.set("content-length", String.valueOf(length));
}
/**
* Returns true if the data associated with this URL can be cached.
*/
public boolean canCache() {
return url.getFile().indexOf('?') < 0 /* && url.postData == null
REMIND */ ;
}
/**
* Call this to close the connection and flush any remaining data.
* Overriders must remember to call super.close()
*/
public void close() {
url = null;
}
private static HashMap<String,Void> proxiedHosts = new HashMap<>();
public static synchronized void setProxiedHost(String host) {
proxiedHosts.put(host.toLowerCase(), null);
}
public static synchronized boolean isProxiedHost(String host) {
return proxiedHosts.containsKey(host.toLowerCase());
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 1994, 1995, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Generic text file handler
*/
package sun.net.www.content.text;
public class Generic extends plain {
/* nothing to do since Generic is identical to plain! */
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 1996, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.content.text;
import java.io.InputStream;
import java.io.FilterInputStream;
/**
* PlainTextInputStream class extends the FilterInputStream class.
* Currently all calls to the PlainTextInputStream object will call
* the corresponding methods in the FilterInputStream class. Hence
* for now its use is more semantic.
*
* @author Sunita Mani
*/
public class PlainTextInputStream extends FilterInputStream {
/**
* Calls FilterInputStream's constructor.
* @param an InputStream
*/
PlainTextInputStream(InputStream is) {
super(is);
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 1994, 1996, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* Plain text file handler.
* @author Steven B. Byrne
*/
package sun.net.www.content.text;
import java.net.*;
import java.io.InputStream;
import java.io.IOException;
public class plain extends ContentHandler {
/**
* Returns a PlainTextInputStream object from which data
* can be read.
*/
public Object getContent(URLConnection uc) {
try {
InputStream is = uc.getInputStream();
return new PlainTextInputStream(uc.getInputStream());
} catch (IOException e) {
return "Error reading document:\n" + e.toString();
}
}
}

View file

@ -0,0 +1,780 @@
/*
* Copyright (c) 1999, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.http;
import java.io.*;
import java.util.*;
import sun.net.*;
import sun.net.www.*;
/**
* A <code>ChunkedInputStream</code> provides a stream for reading a body of
* a http message that can be sent as a series of chunks, each with its own
* size indicator. Optionally the last chunk can be followed by trailers
* containing entity-header fields.
* <p>
* A <code>ChunkedInputStream</code> is also <code>Hurryable</code> so it
* can be hurried to the end of the stream if the bytes are available on
* the underlying stream.
*/
public
class ChunkedInputStream extends InputStream implements Hurryable {
/**
* The underlying stream
*/
private InputStream in;
/**
* The <code>HttpClient</code> that should be notified when the chunked stream has
* completed.
*/
private HttpClient hc;
/**
* The <code>MessageHeader</code> that is populated with any optional trailer
* that appear after the last chunk.
*/
private MessageHeader responses;
/**
* The size, in bytes, of the chunk that is currently being read.
* This size is only valid if the current position in the underlying
* input stream is inside a chunk (ie: state == STATE_READING_CHUNK).
*/
private int chunkSize;
/**
* The number of bytes read from the underlying stream for the current
* chunk. This value is always in the range <code>0</code> through to
* <code>chunkSize</code>
*/
private int chunkRead;
/**
* The internal buffer array where chunk data is available for the
* application to read.
*/
private byte chunkData[] = new byte[4096];
/**
* The current position in the buffer. It contains the index
* of the next byte to read from <code>chunkData</code>
*/
private int chunkPos;
/**
* The index one greater than the index of the last valid byte in the
* buffer. This value is always in the range <code>0</code> through
* <code>chunkData.length</code>.
*/
private int chunkCount;
/**
* The internal buffer where bytes from the underlying stream can be
* read. It may contain bytes representing chunk-size, chunk-data, or
* trailer fields.
*/
private byte rawData[] = new byte[32];
/**
* The current position in the buffer. It contains the index
* of the next byte to read from <code>rawData</code>
*/
private int rawPos;
/**
* The index one greater than the index of the last valid byte in the
* buffer. This value is always in the range <code>0</code> through
* <code>rawData.length</code>.
*/
private int rawCount;
/**
* Indicates if an error was encountered when processing the chunked
* stream.
*/
private boolean error;
/**
* Indicates if the chunked stream has been closed using the
* <code>close</code> method.
*/
private boolean closed;
/*
* Maximum chunk header size of 2KB + 2 bytes for CRLF
*/
private static final int MAX_CHUNK_HEADER_SIZE = 2050;
/**
* State to indicate that next field should be :-
* chunk-size [ chunk-extension ] CRLF
*/
static final int STATE_AWAITING_CHUNK_HEADER = 1;
/**
* State to indicate that we are currently reading the chunk-data.
*/
static final int STATE_READING_CHUNK = 2;
/**
* Indicates that a chunk has been completely read and the next
* fields to be examine should be CRLF
*/
static final int STATE_AWAITING_CHUNK_EOL = 3;
/**
* Indicates that all chunks have been read and the next field
* should be optional trailers or an indication that the chunked
* stream is complete.
*/
static final int STATE_AWAITING_TRAILERS = 4;
/**
* State to indicate that the chunked stream is complete and
* no further bytes should be read from the underlying stream.
*/
static final int STATE_DONE = 5;
/**
* Indicates the current state.
*/
private int state;
/**
* Check to make sure that this stream has not been closed.
*/
private void ensureOpen() throws IOException {
if (closed) {
throw new IOException("stream is closed");
}
}
/**
* Ensures there is <code>size</code> bytes available in
* <code>rawData</code>. This requires that we either
* shift the bytes in use to the begining of the buffer
* or allocate a large buffer with sufficient space available.
*/
private void ensureRawAvailable(int size) {
if (rawCount + size > rawData.length) {
int used = rawCount - rawPos;
if (used + size > rawData.length) {
byte tmp[] = new byte[used + size];
if (used > 0) {
System.arraycopy(rawData, rawPos, tmp, 0, used);
}
rawData = tmp;
} else {
if (used > 0) {
System.arraycopy(rawData, rawPos, rawData, 0, used);
}
}
rawCount = used;
rawPos = 0;
}
}
/**
* Close the underlying input stream by either returning it to the
* keep alive cache or closing the stream.
* <p>
* As a chunked stream is inheritly persistent (see HTTP 1.1 RFC) the
* underlying stream can be returned to the keep alive cache if the
* stream can be completely read without error.
*/
private void closeUnderlying() throws IOException {
if (in == null) {
return;
}
if (!error && state == STATE_DONE) {
hc.finished();
} else {
if (!hurry()) {
hc.closeServer();
}
}
in = null;
}
/**
* Attempt to read the remainder of a chunk directly into the
* caller's buffer.
* <p>
* Return the number of bytes read.
*/
private int fastRead(byte[] b, int off, int len) throws IOException {
// assert state == STATE_READING_CHUNKS;
int remaining = chunkSize - chunkRead;
int cnt = (remaining < len) ? remaining : len;
if (cnt > 0) {
int nread;
try {
nread = in.read(b, off, cnt);
} catch (IOException e) {
error = true;
throw e;
}
if (nread > 0) {
chunkRead += nread;
if (chunkRead >= chunkSize) {
state = STATE_AWAITING_CHUNK_EOL;
}
return nread;
}
error = true;
throw new IOException("Premature EOF");
} else {
return 0;
}
}
/**
* Process any outstanding bytes that have already been read into
* <code>rawData</code>.
* <p>
* The parsing of the chunked stream is performed as a state machine with
* <code>state</code> representing the current state of the processing.
* <p>
* Returns when either all the outstanding bytes in rawData have been
* processed or there is insufficient bytes available to continue
* processing. When the latter occurs <code>rawPos</code> will not have
* been updated and thus the processing can be restarted once further
* bytes have been read into <code>rawData</code>.
*/
private void processRaw() throws IOException {
int pos;
int i;
while (state != STATE_DONE) {
switch (state) {
/**
* We are awaiting a line with a chunk header
*/
case STATE_AWAITING_CHUNK_HEADER:
/*
* Find \n to indicate end of chunk header. If not found when there is
* insufficient bytes in the raw buffer to parse a chunk header.
*/
pos = rawPos;
while (pos < rawCount) {
if (rawData[pos] == '\n') {
break;
}
pos++;
if ((pos - rawPos) >= MAX_CHUNK_HEADER_SIZE) {
error = true;
throw new IOException("Chunk header too long");
}
}
if (pos >= rawCount) {
return;
}
/*
* Extract the chunk size from the header (ignoring extensions).
*/
String header = new String(rawData, rawPos, pos-rawPos+1, "US-ASCII");
for (i=0; i < header.length(); i++) {
if (Character.digit(header.charAt(i), 16) == -1)
break;
}
try {
chunkSize = Integer.parseInt(header, 0, i, 16);
} catch (NumberFormatException e) {
error = true;
throw new IOException("Bogus chunk size");
}
/*
* Chunk has been parsed so move rawPos to first byte of chunk
* data.
*/
rawPos = pos + 1;
chunkRead = 0;
/*
* A chunk size of 0 means EOF.
*/
if (chunkSize > 0) {
state = STATE_READING_CHUNK;
} else {
state = STATE_AWAITING_TRAILERS;
}
break;
/**
* We are awaiting raw entity data (some may have already been
* read). chunkSize is the size of the chunk; chunkRead is the
* total read from the underlying stream to date.
*/
case STATE_READING_CHUNK :
/* no data available yet */
if (rawPos >= rawCount) {
return;
}
/*
* Compute the number of bytes of chunk data available in the
* raw buffer.
*/
int copyLen = Math.min( chunkSize-chunkRead, rawCount-rawPos );
/*
* Expand or compact chunkData if needed.
*/
if (chunkData.length < chunkCount + copyLen) {
int cnt = chunkCount - chunkPos;
if (chunkData.length < cnt + copyLen) {
byte tmp[] = new byte[cnt + copyLen];
System.arraycopy(chunkData, chunkPos, tmp, 0, cnt);
chunkData = tmp;
} else {
System.arraycopy(chunkData, chunkPos, chunkData, 0, cnt);
}
chunkPos = 0;
chunkCount = cnt;
}
/*
* Copy the chunk data into chunkData so that it's available
* to the read methods.
*/
System.arraycopy(rawData, rawPos, chunkData, chunkCount, copyLen);
rawPos += copyLen;
chunkCount += copyLen;
chunkRead += copyLen;
/*
* If all the chunk has been copied into chunkData then the next
* token should be CRLF.
*/
if (chunkSize - chunkRead <= 0) {
state = STATE_AWAITING_CHUNK_EOL;
} else {
return;
}
break;
/**
* Awaiting CRLF after the chunk
*/
case STATE_AWAITING_CHUNK_EOL:
/* not available yet */
if (rawPos + 1 >= rawCount) {
return;
}
if (rawData[rawPos] != '\r') {
error = true;
throw new IOException("missing CR");
}
if (rawData[rawPos+1] != '\n') {
error = true;
throw new IOException("missing LF");
}
rawPos += 2;
/*
* Move onto the next chunk
*/
state = STATE_AWAITING_CHUNK_HEADER;
break;
/**
* Last chunk has been read so not we're waiting for optional
* trailers.
*/
case STATE_AWAITING_TRAILERS:
/*
* Do we have an entire line in the raw buffer?
*/
pos = rawPos;
while (pos < rawCount) {
if (rawData[pos] == '\n') {
break;
}
pos++;
}
if (pos >= rawCount) {
return;
}
if (pos == rawPos) {
error = true;
throw new IOException("LF should be proceeded by CR");
}
if (rawData[pos-1] != '\r') {
error = true;
throw new IOException("LF should be proceeded by CR");
}
/*
* Stream done so close underlying stream.
*/
if (pos == (rawPos + 1)) {
state = STATE_DONE;
closeUnderlying();
return;
}
/*
* Extract any tailers and append them to the message
* headers.
*/
String trailer = new String(rawData, rawPos, pos-rawPos, "US-ASCII");
i = trailer.indexOf(':');
if (i == -1) {
throw new IOException("Malformed tailer - format should be key:value");
}
String key = (trailer.substring(0, i)).trim();
String value = (trailer.substring(i+1, trailer.length())).trim();
responses.add(key, value);
/*
* Move onto the next trailer.
*/
rawPos = pos+1;
break;
} /* switch */
}
}
/**
* Reads any available bytes from the underlying stream into
* <code>rawData</code> and returns the number of bytes of
* chunk data available in <code>chunkData</code> that the
* application can read.
*/
private int readAheadNonBlocking() throws IOException {
/*
* If there's anything available on the underlying stream then we read
* it into the raw buffer and process it. Processing ensures that any
* available chunk data is made available in chunkData.
*/
int avail = in.available();
if (avail > 0) {
/* ensure that there is space in rawData to read the available */
ensureRawAvailable(avail);
int nread;
try {
nread = in.read(rawData, rawCount, avail);
} catch (IOException e) {
error = true;
throw e;
}
if (nread < 0) {
error = true; /* premature EOF ? */
return -1;
}
rawCount += nread;
/*
* Process the raw bytes that have been read.
*/
processRaw();
}
/*
* Return the number of chunked bytes available to read
*/
return chunkCount - chunkPos;
}
/**
* Reads from the underlying stream until there is chunk data
* available in <code>chunkData</code> for the application to
* read.
*/
private int readAheadBlocking() throws IOException {
do {
/*
* All of chunked response has been read to return EOF.
*/
if (state == STATE_DONE) {
return -1;
}
/*
* We must read into the raw buffer so make sure there is space
* available. We use a size of 32 to avoid too much chunk data
* being read into the raw buffer.
*/
ensureRawAvailable(32);
int nread;
try {
nread = in.read(rawData, rawCount, rawData.length-rawCount);
} catch (IOException e) {
error = true;
throw e;
}
/**
* If we hit EOF it means there's a problem as we should never
* attempt to read once the last chunk and trailers have been
* received.
*/
if (nread < 0) {
error = true;
throw new IOException("Premature EOF");
}
/**
* Process the bytes from the underlying stream
*/
rawCount += nread;
processRaw();
} while (chunkCount <= 0);
/*
* Return the number of chunked bytes available to read
*/
return chunkCount - chunkPos;
}
/**
* Read ahead in either blocking or non-blocking mode. This method
* is typically used when we run out of available bytes in
* <code>chunkData</code> or we need to determine how many bytes
* are available on the input stream.
*/
private int readAhead(boolean allowBlocking) throws IOException {
/*
* Last chunk already received - return EOF
*/
if (state == STATE_DONE) {
return -1;
}
/*
* Reset position/count if data in chunkData is exhausted.
*/
if (chunkPos >= chunkCount) {
chunkCount = 0;
chunkPos = 0;
}
/*
* Read ahead blocking or non-blocking
*/
if (allowBlocking) {
return readAheadBlocking();
} else {
return readAheadNonBlocking();
}
}
/**
* Creates a <code>ChunkedInputStream</code> and saves its arguments, for
* later use.
*
* @param in the underlying input stream.
* @param hc the HttpClient
* @param responses the MessageHeader that should be populated with optional
* trailers.
*/
public ChunkedInputStream(InputStream in, HttpClient hc, MessageHeader responses) throws IOException {
/* save arguments */
this.in = in;
this.responses = responses;
this.hc = hc;
/*
* Set our initial state to indicate that we are first starting to
* look for a chunk header.
*/
state = STATE_AWAITING_CHUNK_HEADER;
}
/**
* See
* the general contract of the <code>read</code>
* method of <code>InputStream</code>.
*
* @return the next byte of data, or <code>-1</code> if the end of the
* stream is reached.
* @exception IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
public synchronized int read() throws IOException {
ensureOpen();
if (chunkPos >= chunkCount) {
if (readAhead(true) <= 0) {
return -1;
}
}
return chunkData[chunkPos++] & 0xff;
}
/**
* Reads bytes from this stream into the specified byte array, starting at
* the given offset.
*
* @param b destination buffer.
* @param off offset at which to start storing bytes.
* @param len maximum number of bytes to read.
* @return the number of bytes read, or <code>-1</code> if the end of
* the stream has been reached.
* @exception IOException if an I/O error occurs.
*/
public synchronized int read(byte b[], int off, int len)
throws IOException
{
ensureOpen();
if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int avail = chunkCount - chunkPos;
if (avail <= 0) {
/*
* Optimization: if we're in the middle of the chunk read
* directly from the underlying stream into the caller's
* buffer
*/
if (state == STATE_READING_CHUNK) {
return fastRead( b, off, len );
}
/*
* We're not in the middle of a chunk so we must read ahead
* until there is some chunk data available.
*/
avail = readAhead(true);
if (avail < 0) {
return -1; /* EOF */
}
}
int cnt = (avail < len) ? avail : len;
System.arraycopy(chunkData, chunkPos, b, off, cnt);
chunkPos += cnt;
return cnt;
}
/**
* Returns the number of bytes that can be read from this input
* stream without blocking.
*
* @return the number of bytes that can be read from this input
* stream without blocking.
* @exception IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
public synchronized int available() throws IOException {
ensureOpen();
int avail = chunkCount - chunkPos;
if(avail > 0) {
return avail;
}
avail = readAhead(false);
if (avail < 0) {
return 0;
} else {
return avail;
}
}
/**
* Close the stream by either returning the connection to the
* keep alive cache or closing the underlying stream.
* <p>
* If the chunked response hasn't been completely read we
* try to "hurry" to the end of the response. If this is
* possible (without blocking) then the connection can be
* returned to the keep alive cache.
*
* @exception IOException if an I/O error occurs.
*/
public synchronized void close() throws IOException {
if (closed) {
return;
}
closeUnderlying();
closed = true;
}
/**
* Hurry the input stream by reading everything from the underlying
* stream. If the last chunk (and optional trailers) can be read without
* blocking then the stream is considered hurried.
* <p>
* Note that if an error has occurred or we can't get to last chunk
* without blocking then this stream can't be hurried and should be
* closed.
*/
public synchronized boolean hurry() {
if (in == null || error) {
return false;
}
try {
readAhead(false);
} catch (Exception e) {
return false;
}
if (error) {
return false;
}
return (state == STATE_DONE);
}
}

View file

@ -0,0 +1,300 @@
/*
* Copyright (c) 2004, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.http;
import java.io.*;
/**
* OutputStream that sends the output to the underlying stream using chunked
* encoding as specified in RFC 2068.
*/
public class ChunkedOutputStream extends PrintStream {
/* Default chunk size (including chunk header) if not specified */
static final int DEFAULT_CHUNK_SIZE = 4096;
private static final byte[] CRLF = {'\r', '\n'};
private static final int CRLF_SIZE = CRLF.length;
private static final byte[] FOOTER = CRLF;
private static final int FOOTER_SIZE = CRLF_SIZE;
private static final byte[] EMPTY_CHUNK_HEADER = getHeader(0);
private static final int EMPTY_CHUNK_HEADER_SIZE = getHeaderSize(0);
/* internal buffer */
private byte buf[];
/* size of data (excluding footers and headers) already stored in buf */
private int size;
/* current index in buf (i.e. buf[count] */
private int count;
/* number of bytes to be filled up to complete a data chunk
* currently being built */
private int spaceInCurrentChunk;
/* underlying stream */
private PrintStream out;
/* the chunk size we use */
private int preferredChunkDataSize;
private int preferedHeaderSize;
private int preferredChunkGrossSize;
/* header for a complete Chunk */
private byte[] completeHeader;
/* return the size of the header for a particular chunk size */
private static int getHeaderSize(int size) {
return (Integer.toHexString(size)).length() + CRLF_SIZE;
}
/* return a header for a particular chunk size */
private static byte[] getHeader(int size){
try {
String hexStr = Integer.toHexString(size);
byte[] hexBytes = hexStr.getBytes("US-ASCII");
byte[] header = new byte[getHeaderSize(size)];
for (int i=0; i<hexBytes.length; i++)
header[i] = hexBytes[i];
header[hexBytes.length] = CRLF[0];
header[hexBytes.length+1] = CRLF[1];
return header;
} catch (java.io.UnsupportedEncodingException e) {
/* This should never happen */
throw new InternalError(e.getMessage(), e);
}
}
public ChunkedOutputStream(PrintStream o) {
this(o, DEFAULT_CHUNK_SIZE);
}
public ChunkedOutputStream(PrintStream o, int size) {
super(o);
out = o;
if (size <= 0) {
size = DEFAULT_CHUNK_SIZE;
}
/* Adjust the size to cater for the chunk header - eg: if the
* preferred chunk size is 1k this means the chunk size should
* be 1017 bytes (differs by 7 from preferred size because of
* 3 bytes for chunk size in hex and CRLF (header) and CRLF (footer)).
*
* If headerSize(adjusted_size) is shorter then headerSize(size)
* then try to use the extra byte unless headerSize(adjusted_size+1)
* increases back to headerSize(size)
*/
if (size > 0) {
int adjusted_size = size - getHeaderSize(size) - FOOTER_SIZE;
if (getHeaderSize(adjusted_size+1) < getHeaderSize(size)){
adjusted_size++;
}
size = adjusted_size;
}
if (size > 0) {
preferredChunkDataSize = size;
} else {
preferredChunkDataSize = DEFAULT_CHUNK_SIZE -
getHeaderSize(DEFAULT_CHUNK_SIZE) - FOOTER_SIZE;
}
preferedHeaderSize = getHeaderSize(preferredChunkDataSize);
preferredChunkGrossSize = preferedHeaderSize + preferredChunkDataSize
+ FOOTER_SIZE;
completeHeader = getHeader(preferredChunkDataSize);
/* start with an initial buffer */
buf = new byte[preferredChunkGrossSize];
reset();
}
/*
* Flush a buffered, completed chunk to an underlying stream. If the data in
* the buffer is insufficient to build up a chunk of "preferredChunkSize"
* then the data do not get flushed unless flushAll is true. If flushAll is
* true then the remaining data builds up a last chunk which size is smaller
* than preferredChunkSize, and then the last chunk gets flushed to
* underlying stream. If flushAll is true and there is no data in a buffer
* at all then an empty chunk (containing a header only) gets flushed to
* underlying stream.
*/
private void flush(boolean flushAll) {
if (spaceInCurrentChunk == 0) {
/* flush a completed chunk to underlying stream */
out.write(buf, 0, preferredChunkGrossSize);
out.flush();
reset();
} else if (flushAll){
/* complete the last chunk and flush it to underlying stream */
if (size > 0){
/* adjust a header start index in case the header of the last
* chunk is shorter then preferedHeaderSize */
int adjustedHeaderStartIndex = preferedHeaderSize -
getHeaderSize(size);
/* write header */
System.arraycopy(getHeader(size), 0, buf,
adjustedHeaderStartIndex, getHeaderSize(size));
/* write footer */
buf[count++] = FOOTER[0];
buf[count++] = FOOTER[1];
//send the last chunk to underlying stream
out.write(buf, adjustedHeaderStartIndex, count - adjustedHeaderStartIndex);
} else {
//send an empty chunk (containing just a header) to underlying stream
out.write(EMPTY_CHUNK_HEADER, 0, EMPTY_CHUNK_HEADER_SIZE);
}
out.flush();
reset();
}
}
@Override
public boolean checkError() {
return out.checkError();
}
/* Check that the output stream is still open */
private void ensureOpen() {
if (out == null)
setError();
}
/*
* Writes data from b[] to an internal buffer and stores the data as data
* chunks of a following format: {Data length in Hex}{CRLF}{data}{CRLF}
* The size of the data is preferredChunkSize. As soon as a completed chunk
* is read from b[] a process of reading from b[] suspends, the chunk gets
* flushed to the underlying stream and then the reading process from b[]
* continues. When there is no more sufficient data in b[] to build up a
* chunk of preferredChunkSize size the data get stored as an incomplete
* chunk of a following format: {space for data length}{CRLF}{data}
* The size of the data is of course smaller than preferredChunkSize.
*/
@Override
public synchronized void write(byte b[], int off, int len) {
ensureOpen();
if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
/* if b[] contains enough data then one loop cycle creates one complete
* data chunk with a header, body and a footer, and then flushes the
* chunk to the underlying stream. Otherwise, the last loop cycle
* creates incomplete data chunk with empty header and with no footer
* and stores this incomplete chunk in an internal buffer buf[]
*/
int bytesToWrite = len;
int inputIndex = off; /* the index of the byte[] currently being written */
do {
/* enough data to complete a chunk */
if (bytesToWrite >= spaceInCurrentChunk) {
/* header */
for (int i=0; i<completeHeader.length; i++)
buf[i] = completeHeader[i];
/* data */
System.arraycopy(b, inputIndex, buf, count, spaceInCurrentChunk);
inputIndex += spaceInCurrentChunk;
bytesToWrite -= spaceInCurrentChunk;
count += spaceInCurrentChunk;
/* footer */
buf[count++] = FOOTER[0];
buf[count++] = FOOTER[1];
spaceInCurrentChunk = 0; //chunk is complete
flush(false);
if (checkError()){
break;
}
}
/* not enough data to build a chunk */
else {
/* header */
/* do not write header if not enough bytes to build a chunk yet */
/* data */
System.arraycopy(b, inputIndex, buf, count, bytesToWrite);
count += bytesToWrite;
size += bytesToWrite;
spaceInCurrentChunk -= bytesToWrite;
bytesToWrite = 0;
/* footer */
/* do not write header if not enough bytes to build a chunk yet */
}
} while (bytesToWrite > 0);
}
@Override
public synchronized void write(int _b) {
byte b[] = {(byte)_b};
write(b, 0, 1);
}
public synchronized void reset() {
count = preferedHeaderSize;
size = 0;
spaceInCurrentChunk = preferredChunkDataSize;
}
public int size() {
return size;
}
@Override
public synchronized void close() {
ensureOpen();
/* if we have buffer a chunked send it */
if (size > 0) {
flush(true);
}
/* send a zero length chunk */
flush(true);
/* don't close the underlying stream */
out = null;
}
@Override
public synchronized void flush() {
ensureOpen();
if (size > 0) {
flush(true);
}
}
}

View file

@ -0,0 +1,171 @@
/*
* Copyright (c) 2009, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.http;
import java.io.*;
import java.util.ArrayList;
import java.util.regex.*;
import sun.net.NetProperties;
import sun.util.logging.PlatformLogger;
/**
* Main class of the HTTP traffic capture tool.
* Captures are triggered by the sun.net.http.captureRules system property.
* If set, it should point to a file containing the capture rules.
* Format for the file is simple:
* - 1 rule per line
* - Lines starting with a # are considered comments and ignored
* - a rule is a pair of a regular expression and file pattern, separated by a comma
* - The regular expression is applied to URLs, if it matches, the traffic for
* that URL will be captured in the associated file.
* - if the file name contains a '%d', then that sequence will be replaced by a
* unique random number for each URL. This allow for multi-threaded captures
* of URLs matching the same pattern.
* - Rules are checked in sequence, in the same order as in the file, until a
* match is found or the end of the list is reached.
*
* Examples of rules:
* www\.sun\.com , sun%d.log
* yahoo\.com\/.*asf , yahoo.log
*
* @author jccollet
*/
public class HttpCapture {
private File file;
private boolean incoming = true;
private BufferedWriter out;
private static boolean initialized;
private static volatile ArrayList<Pattern> patterns;
private static volatile ArrayList<String> capFiles;
private static synchronized void init() {
initialized = true;
String rulesFile = java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<>() {
public String run() {
return NetProperties.get("sun.net.http.captureRules");
}
});
if (rulesFile != null && !rulesFile.isEmpty()) {
BufferedReader in;
try {
in = new BufferedReader(new FileReader(rulesFile));
} catch (FileNotFoundException ex) {
return;
}
try {
String line = in.readLine();
while (line != null) {
line = line.trim();
if (!line.startsWith("#")) {
// skip line if it's a comment
String[] s = line.split(",");
if (s.length == 2) {
if (patterns == null) {
patterns = new ArrayList<>();
capFiles = new ArrayList<>();
}
patterns.add(Pattern.compile(s[0].trim()));
capFiles.add(s[1].trim());
}
}
line = in.readLine();
}
} catch (IOException ioe) {
} finally {
try {
in.close();
} catch (IOException ex) {
}
}
}
}
private static synchronized boolean isInitialized() {
return initialized;
}
private HttpCapture(File f, java.net.URL url) {
file = f;
try {
out = new BufferedWriter(new FileWriter(file, true));
out.write("URL: " + url + "\n");
} catch (IOException ex) {
PlatformLogger.getLogger(HttpCapture.class.getName()).severe(null, ex);
}
}
public synchronized void sent(int c) throws IOException {
if (incoming) {
out.write("\n------>\n");
incoming = false;
out.flush();
}
out.write(c);
}
public synchronized void received(int c) throws IOException {
if (!incoming) {
out.write("\n<------\n");
incoming = true;
out.flush();
}
out.write(c);
}
public synchronized void flush() throws IOException {
out.flush();
}
public static HttpCapture getCapture(java.net.URL url) {
if (!isInitialized()) {
init();
}
if (patterns == null || patterns.isEmpty()) {
return null;
}
String s = url.toString();
for (int i = 0; i < patterns.size(); i++) {
Pattern p = patterns.get(i);
if (p.matcher(s).find()) {
String f = capFiles.get(i);
File fi;
if (f.indexOf("%d") >= 0) {
java.util.Random rand = new java.util.Random();
do {
String f2 = f.replace("%d", Integer.toString(rand.nextInt()));
fi = new File(f2);
} while (fi.exists());
} else {
fi = new File(f);
}
return new HttpCapture(fi, url);
}
}
return null;
}
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2009, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.http;
import java.io.*;
/**
* A Simple FilterInputStream subclass to capture HTTP traffic.
* Every byte read is also passed to the HttpCapture class.
*
* @author jccollet
*/
public class HttpCaptureInputStream extends FilterInputStream {
private HttpCapture capture = null;
public HttpCaptureInputStream(InputStream in, HttpCapture cap) {
super(in);
capture = cap;
}
@Override
public int read() throws IOException {
int i = super.read();
capture.received(i);
return i;
}
@Override
public void close() throws IOException {
try {
capture.flush();
} catch (IOException iOException) {
}
super.close();
}
@Override
public int read(byte[] b) throws IOException {
int ret = super.read(b);
for (int i = 0; i < ret; i++) {
capture.received(b[i]);
}
return ret;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int ret = super.read(b, off, len);
for (int i = 0; i < ret; i++) {
capture.received(b[off+i]);
}
return ret;
}
}

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2009, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.http;
import java.io.*;
/**
* A Simple FilterOutputStream subclass to capture HTTP traffic.
* Every byte written is also passed to the HttpCapture class.
*
* @author jccollet
*/
public class HttpCaptureOutputStream extends FilterOutputStream {
private HttpCapture capture = null;
public HttpCaptureOutputStream(OutputStream out, HttpCapture cap) {
super(out);
capture = cap;
}
@Override
public void write(int b) throws IOException {
capture.sent(b);
out.write(b);
}
@Override
public void write(byte[] ba) throws IOException {
for (byte b : ba) {
capture.sent(b);
}
out.write(ba);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
for (int i = off; i < len; i++) {
capture.sent(b[i]);
}
out.write(b, off, len);
}
@Override
public void flush() throws IOException {
try {
capture.flush();
} catch (IOException iOException) {
}
super.flush();
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2001, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.http;
/**
* A <code>Hurryable</code> is a class that has been instructed to complete
* its input processing so as to make resource associated with that input
* available to others.
*/
public interface Hurryable {
/**
* @return a <code>boolean</code> indicating if the stream has been
* hurried or not.
*/
boolean hurry();
}

View file

@ -0,0 +1,337 @@
/*
* Copyright (c) 1996, 2011, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.http;
import java.io.IOException;
import java.io.NotSerializableException;
import java.util.ArrayList;
import java.util.HashMap;
import java.net.URL;
import jdk.internal.misc.InnocuousThread;
/**
* A class that implements a cache of idle Http connections for keep-alive
*
* @author Stephen R. Pietrowicz (NCSA)
* @author Dave Brown
*/
public class KeepAliveCache
extends HashMap<KeepAliveKey, ClientVector>
implements Runnable {
private static final long serialVersionUID = -2937172892064557949L;
/* maximum # keep-alive connections to maintain at once
* This should be 2 by the HTTP spec, but because we don't support pipe-lining
* a larger value is more appropriate. So we now set a default of 5, and the value
* refers to the number of idle connections per destination (in the cache) only.
* It can be reset by setting system property "http.maxConnections".
*/
static final int MAX_CONNECTIONS = 5;
static int result = -1;
static int getMaxConnections() {
if (result == -1) {
result = java.security.AccessController.doPrivileged(
new sun.security.action.GetIntegerAction("http.maxConnections",
MAX_CONNECTIONS))
.intValue();
if (result <= 0)
result = MAX_CONNECTIONS;
}
return result;
}
static final int LIFETIME = 5000;
private Thread keepAliveTimer = null;
/**
* Constructor
*/
public KeepAliveCache() {}
/**
* Register this URL and HttpClient (that supports keep-alive) with the cache
* @param url The URL contains info about the host and port
* @param http The HttpClient to be cached
*/
public synchronized void put(final URL url, Object obj, HttpClient http) {
boolean startThread = (keepAliveTimer == null);
if (!startThread) {
if (!keepAliveTimer.isAlive()) {
startThread = true;
}
}
if (startThread) {
clear();
/* Unfortunately, we can't always believe the keep-alive timeout we got
* back from the server. If I'm connected through a Netscape proxy
* to a server that sent me a keep-alive
* time of 15 sec, the proxy unilaterally terminates my connection
* The robustness to get around this is in HttpClient.parseHTTP()
*/
final KeepAliveCache cache = this;
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<>() {
public Void run() {
keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache);
keepAliveTimer.setDaemon(true);
keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2);
keepAliveTimer.start();
return null;
}
});
}
KeepAliveKey key = new KeepAliveKey(url, obj);
ClientVector v = super.get(key);
if (v == null) {
int keepAliveTimeout = http.getKeepAliveTimeout();
v = new ClientVector(keepAliveTimeout > 0?
keepAliveTimeout*1000 : LIFETIME);
v.put(http);
super.put(key, v);
} else {
v.put(http);
}
}
/* remove an obsolete HttpClient from its VectorCache */
public synchronized void remove (HttpClient h, Object obj) {
KeepAliveKey key = new KeepAliveKey(h.url, obj);
ClientVector v = super.get(key);
if (v != null) {
v.remove(h);
if (v.empty()) {
removeVector(key);
}
}
}
/* called by a clientVector thread when all its connections have timed out
* and that vector of connections should be removed.
*/
synchronized void removeVector(KeepAliveKey k) {
super.remove(k);
}
/**
* Check to see if this URL has a cached HttpClient
*/
public synchronized HttpClient get(URL url, Object obj) {
KeepAliveKey key = new KeepAliveKey(url, obj);
ClientVector v = super.get(key);
if (v == null) { // nothing in cache yet
return null;
}
return v.get();
}
/* Sleeps for an alloted timeout, then checks for timed out connections.
* Errs on the side of caution (leave connections idle for a relatively
* short time).
*/
@Override
public void run() {
do {
try {
Thread.sleep(LIFETIME);
} catch (InterruptedException e) {}
synchronized (this) {
/* Remove all unused HttpClients. Starting from the
* bottom of the stack (the least-recently used first).
* REMIND: It'd be nice to not remove *all* connections
* that aren't presently in use. One could have been added
* a second ago that's still perfectly valid, and we're
* needlessly axing it. But it's not clear how to do this
* cleanly, and doing it right may be more trouble than it's
* worth.
*/
long currentTime = System.currentTimeMillis();
ArrayList<KeepAliveKey> keysToRemove
= new ArrayList<>();
for (KeepAliveKey key : keySet()) {
ClientVector v = get(key);
synchronized (v) {
int i;
for (i = 0; i < v.size(); i++) {
KeepAliveEntry e = v.elementAt(i);
if ((currentTime - e.idleStartTime) > v.nap) {
HttpClient h = e.hc;
h.closeServer();
} else {
break;
}
}
v.subList(0, i).clear();
if (v.size() == 0) {
keysToRemove.add(key);
}
}
}
for (KeepAliveKey key : keysToRemove) {
removeVector(key);
}
}
} while (size() > 0);
return;
}
/*
* Do not serialize this class!
*/
private void writeObject(java.io.ObjectOutputStream stream)
throws IOException {
throw new NotSerializableException();
}
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException {
throw new NotSerializableException();
}
}
/* FILO order for recycling HttpClients, should run in a thread
* to time them out. If > maxConns are in use, block.
*/
class ClientVector extends java.util.Stack<KeepAliveEntry> {
private static final long serialVersionUID = -8680532108106489459L;
// sleep time in milliseconds, before cache clear
int nap;
ClientVector (int nap) {
this.nap = nap;
}
synchronized HttpClient get() {
if (empty()) {
return null;
} else {
// Loop until we find a connection that has not timed out
HttpClient hc = null;
long currentTime = System.currentTimeMillis();
do {
KeepAliveEntry e = pop();
if ((currentTime - e.idleStartTime) > nap) {
e.hc.closeServer();
} else {
hc = e.hc;
}
} while ((hc== null) && (!empty()));
return hc;
}
}
/* return a still valid, unused HttpClient */
synchronized void put(HttpClient h) {
if (size() >= KeepAliveCache.getMaxConnections()) {
h.closeServer(); // otherwise the connection remains in limbo
} else {
push(new KeepAliveEntry(h, System.currentTimeMillis()));
}
}
/*
* Do not serialize this class!
*/
private void writeObject(java.io.ObjectOutputStream stream)
throws IOException {
throw new NotSerializableException();
}
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException {
throw new NotSerializableException();
}
}
class KeepAliveKey {
private String protocol = null;
private String host = null;
private int port = 0;
private Object obj = null; // additional key, such as socketfactory
/**
* Constructor
*
* @param url the URL containing the protocol, host and port information
*/
public KeepAliveKey(URL url, Object obj) {
this.protocol = url.getProtocol();
this.host = url.getHost();
this.port = url.getPort();
this.obj = obj;
}
/**
* Determine whether or not two objects of this type are equal
*/
@Override
public boolean equals(Object obj) {
if ((obj instanceof KeepAliveKey) == false)
return false;
KeepAliveKey kae = (KeepAliveKey)obj;
return host.equals(kae.host)
&& (port == kae.port)
&& protocol.equals(kae.protocol)
&& this.obj == kae.obj;
}
/**
* The hashCode() for this object is the string hashCode() of
* concatenation of the protocol, host name and port.
*/
@Override
public int hashCode() {
String str = protocol+host+port;
return this.obj == null? str.hashCode() :
str.hashCode() + this.obj.hashCode();
}
}
class KeepAliveEntry {
HttpClient hc;
long idleStartTime;
KeepAliveEntry(HttpClient hc, long idleStartTime) {
this.hc = hc;
this.idleStartTime = idleStartTime;
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 1996, 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.http;
import java.io.*;
class KeepAliveCleanerEntry
{
KeepAliveStream kas;
HttpClient hc;
public KeepAliveCleanerEntry(KeepAliveStream kas, HttpClient hc) {
this.kas = kas;
this.hc = hc;
}
protected KeepAliveStream getKeepAliveStream() {
return kas;
}
protected HttpClient getHttpClient() {
return hc;
}
protected void setQueuedForCleanup() {
kas.queuedForCleanup = true;
}
protected boolean getQueuedForCleanup() {
return kas.queuedForCleanup;
}
}

View file

@ -0,0 +1,196 @@
/*
* Copyright (c) 1996, 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.http;
import java.io.*;
import sun.net.ProgressSource;
import sun.net.www.MeteredStream;
import jdk.internal.misc.InnocuousThread;
/**
* A stream that has the property of being able to be kept alive for
* multiple downloads from the same server.
*
* @author Stephen R. Pietrowicz (NCSA)
* @author Dave Brown
*/
public
class KeepAliveStream extends MeteredStream implements Hurryable {
// instance variables
HttpClient hc;
boolean hurried;
// has this KeepAliveStream been put on the queue for asynchronous cleanup.
protected boolean queuedForCleanup = false;
private static final KeepAliveStreamCleaner queue = new KeepAliveStreamCleaner();
private static Thread cleanerThread; // null
/**
* Constructor
*/
public KeepAliveStream(InputStream is, ProgressSource pi, long expected, HttpClient hc) {
super(is, pi, expected);
this.hc = hc;
}
/**
* Attempt to cache this connection
*/
public void close() throws IOException {
// If the inputstream is closed already, just return.
if (closed) {
return;
}
// If this stream has already been queued for cleanup.
if (queuedForCleanup) {
return;
}
// Skip past the data that's left in the Inputstream because
// some sort of error may have occurred.
// Do this ONLY if the skip won't block. The stream may have
// been closed at the beginning of a big file and we don't want
// to hang around for nothing. So if we can't skip without blocking
// we just close the socket and, therefore, terminate the keepAlive
// NOTE: Don't close super class
try {
if (expected > count) {
long nskip = expected - count;
if (nskip <= available()) {
do {} while ((nskip = (expected - count)) > 0L
&& skip(Math.min(nskip, available())) > 0L);
} else if (expected <= KeepAliveStreamCleaner.MAX_DATA_REMAINING && !hurried) {
//put this KeepAliveStream on the queue so that the data remaining
//on the socket can be cleanup asyncronously.
queueForCleanup(new KeepAliveCleanerEntry(this, hc));
} else {
hc.closeServer();
}
}
if (!closed && !hurried && !queuedForCleanup) {
hc.finished();
}
} finally {
if (pi != null)
pi.finishTracking();
if (!queuedForCleanup) {
// nulling out the underlying inputstream as well as
// httpClient to let gc collect the memories faster
in = null;
hc = null;
closed = true;
}
}
}
/* we explicitly do not support mark/reset */
public boolean markSupported() {
return false;
}
public void mark(int limit) {}
public void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
public synchronized boolean hurry() {
try {
/* CASE 0: we're actually already done */
if (closed || count >= expected) {
return false;
} else if (in.available() < (expected - count)) {
/* CASE I: can't meet the demand */
return false;
} else {
/* CASE II: fill our internal buffer
* Remind: possibly check memory here
*/
int size = (int) (expected - count);
byte[] buf = new byte[size];
DataInputStream dis = new DataInputStream(in);
dis.readFully(buf);
in = new ByteArrayInputStream(buf);
hurried = true;
return true;
}
} catch (IOException e) {
// e.printStackTrace();
return false;
}
}
private static void queueForCleanup(KeepAliveCleanerEntry kace) {
synchronized(queue) {
if(!kace.getQueuedForCleanup()) {
if (!queue.offer(kace)) {
kace.getHttpClient().closeServer();
return;
}
kace.setQueuedForCleanup();
queue.notifyAll();
}
boolean startCleanupThread = (cleanerThread == null);
if (!startCleanupThread) {
if (!cleanerThread.isAlive()) {
startCleanupThread = true;
}
}
if (startCleanupThread) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
cleanerThread = InnocuousThread.newSystemThread("Keep-Alive-SocketCleaner", queue);
cleanerThread.setDaemon(true);
cleanerThread.setPriority(Thread.MAX_PRIORITY - 2);
cleanerThread.start();
return null;
}
});
}
} // queue
}
protected long remainingToRead() {
return expected - count;
}
protected void setClosed() {
in = null;
hc = null;
closed = true;
}
}

View file

@ -0,0 +1,155 @@
/*
* Copyright (c) 2005, 2008, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.http;
import java.io.IOException;
import java.util.LinkedList;
import sun.net.NetProperties;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* This class is used to cleanup any remaining data that may be on a KeepAliveStream
* so that the connection can be cached in the KeepAliveCache.
* Instances of this class can be used as a FIFO queue for KeepAliveCleanerEntry objects.
* Executing this Runnable removes each KeepAliveCleanerEntry from the Queue, reads
* the reamining bytes on its KeepAliveStream, and if successful puts the connection in
* the KeepAliveCache.
*
* @author Chris Hegarty
*/
@SuppressWarnings("serial") // never serialized
class KeepAliveStreamCleaner
extends LinkedList<KeepAliveCleanerEntry>
implements Runnable
{
// maximum amount of remaining data that we will try to cleanup
protected static int MAX_DATA_REMAINING = 512;
// maximum amount of KeepAliveStreams to be queued
protected static int MAX_CAPACITY = 10;
// timeout for both socket and poll on the queue
protected static final int TIMEOUT = 5000;
// max retries for skipping data
private static final int MAX_RETRIES = 5;
static {
final String maxDataKey = "http.KeepAlive.remainingData";
int maxData = AccessController.doPrivileged(
new PrivilegedAction<Integer>() {
public Integer run() {
return NetProperties.getInteger(maxDataKey, MAX_DATA_REMAINING);
}}).intValue() * 1024;
MAX_DATA_REMAINING = maxData;
final String maxCapacityKey = "http.KeepAlive.queuedConnections";
int maxCapacity = AccessController.doPrivileged(
new PrivilegedAction<Integer>() {
public Integer run() {
return NetProperties.getInteger(maxCapacityKey, MAX_CAPACITY);
}}).intValue();
MAX_CAPACITY = maxCapacity;
}
@Override
public boolean offer(KeepAliveCleanerEntry e) {
if (size() >= MAX_CAPACITY)
return false;
return super.offer(e);
}
@Override
public void run()
{
KeepAliveCleanerEntry kace = null;
do {
try {
synchronized(this) {
long before = System.currentTimeMillis();
long timeout = TIMEOUT;
while ((kace = poll()) == null) {
this.wait(timeout);
long after = System.currentTimeMillis();
long elapsed = after - before;
if (elapsed > timeout) {
/* one last try */
kace = poll();
break;
}
before = after;
timeout -= elapsed;
}
}
if(kace == null)
break;
KeepAliveStream kas = kace.getKeepAliveStream();
if (kas != null) {
synchronized(kas) {
HttpClient hc = kace.getHttpClient();
try {
if (hc != null && !hc.isInKeepAliveCache()) {
int oldTimeout = hc.getReadTimeout();
hc.setReadTimeout(TIMEOUT);
long remainingToRead = kas.remainingToRead();
if (remainingToRead > 0) {
long n = 0;
int retries = 0;
while (n < remainingToRead && retries < MAX_RETRIES) {
remainingToRead = remainingToRead - n;
n = kas.skip(remainingToRead);
if (n == 0)
retries++;
}
remainingToRead = remainingToRead - n;
}
if (remainingToRead == 0) {
hc.setReadTimeout(oldTimeout);
hc.finished();
} else
hc.closeServer();
}
} catch (IOException ioe) {
hc.closeServer();
} finally {
kas.setClosed();
}
}
}
} catch (InterruptedException ie) { }
} while (kace != null);
}
}

View file

@ -0,0 +1,105 @@
/*
* Copyright (c) 2001, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.http;
import java.io.*;
import java.net.*;
/**
* Instances of this class are returned to applications for the purpose of
* sending user data for a HTTP request (excluding TRACE). This class is used
* when the content-length will be specified in the header of the request.
* The semantics of ByteArrayOutputStream are extended so that
* when close() is called, it is no longer possible to write
* additional data to the stream. From this point the content length of
* the request is fixed and cannot change.
*
* @author Michael McMahon
*/
public class PosterOutputStream extends ByteArrayOutputStream {
private boolean closed;
/**
* Creates a new output stream for POST user data
*/
public PosterOutputStream () {
super (256);
}
/**
* Writes the specified byte to this output stream.
*
* @param b the byte to be written.
*/
public synchronized void write(int b) {
if (closed) {
return;
}
super.write (b);
}
/**
* Writes <code>len</code> bytes from the specified byte array
* starting at offset <code>off</code> to this output stream.
*
* @param b the data.
* @param off the start offset in the data.
* @param len the number of bytes to write.
*/
public synchronized void write(byte b[], int off, int len) {
if (closed) {
return;
}
super.write (b, off, len);
}
/**
* Resets the <code>count</code> field of this output
* stream to zero, so that all currently accumulated output in the
* output stream is discarded. The output stream can be used again,
* reusing the already allocated buffer space. If the output stream
* has been closed, then this method has no effect.
*
* @see java.io.ByteArrayInputStream#count
*/
public synchronized void reset() {
if (closed) {
return;
}
super.reset ();
}
/**
* After close() has been called, it is no longer possible to write
* to this stream. Further calls to write will have no effect.
*/
public synchronized void close() throws IOException {
closed = true;
super.close ();
}
}

View file

@ -0,0 +1,234 @@
/*
* Copyright (c) 1995, 2010, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* Open an file input stream given a URL.
* @author James Gosling
* @author Steven B. Byrne
*/
package sun.net.www.protocol.file;
import java.net.URL;
import java.net.FileNameMap;
import java.io.*;
import java.text.Collator;
import java.security.Permission;
import sun.net.*;
import sun.net.www.*;
import java.util.*;
import java.text.SimpleDateFormat;
public class FileURLConnection extends URLConnection {
static String CONTENT_LENGTH = "content-length";
static String CONTENT_TYPE = "content-type";
static String TEXT_PLAIN = "text/plain";
static String LAST_MODIFIED = "last-modified";
String contentType;
InputStream is;
File file;
String filename;
boolean isDirectory = false;
boolean exists = false;
List<String> files;
long length = -1;
long lastModified = 0;
protected FileURLConnection(URL u, File file) {
super(u);
this.file = file;
}
/*
* Note: the semantics of FileURLConnection object is that the
* results of the various URLConnection calls, such as
* getContentType, getInputStream or getContentLength reflect
* whatever was true when connect was called.
*/
public void connect() throws IOException {
if (!connected) {
try {
filename = file.toString();
isDirectory = file.isDirectory();
if (isDirectory) {
String[] fileList = file.list();
if (fileList == null)
throw new FileNotFoundException(filename + " exists, but is not accessible");
files = Arrays.<String>asList(fileList);
} else {
is = new BufferedInputStream(new FileInputStream(filename));
// Check if URL should be metered
boolean meteredInput = ProgressMonitor.getDefault().shouldMeterInput(url, "GET");
if (meteredInput) {
ProgressSource pi = new ProgressSource(url, "GET", file.length());
is = new MeteredStream(is, pi, file.length());
}
}
} catch (IOException e) {
throw e;
}
connected = true;
}
}
private boolean initializedHeaders = false;
private void initializeHeaders() {
try {
connect();
exists = file.exists();
} catch (IOException e) {
}
if (!initializedHeaders || !exists) {
length = file.length();
lastModified = file.lastModified();
if (!isDirectory) {
FileNameMap map = java.net.URLConnection.getFileNameMap();
contentType = map.getContentTypeFor(filename);
if (contentType != null) {
properties.add(CONTENT_TYPE, contentType);
}
properties.add(CONTENT_LENGTH, String.valueOf(length));
/*
* Format the last-modified field into the preferred
* Internet standard - ie: fixed-length subset of that
* defined by RFC 1123
*/
if (lastModified != 0) {
Date date = new Date(lastModified);
SimpleDateFormat fo =
new SimpleDateFormat ("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
fo.setTimeZone(TimeZone.getTimeZone("GMT"));
properties.add(LAST_MODIFIED, fo.format(date));
}
} else {
properties.add(CONTENT_TYPE, TEXT_PLAIN);
}
initializedHeaders = true;
}
}
public String getHeaderField(String name) {
initializeHeaders();
return super.getHeaderField(name);
}
public String getHeaderField(int n) {
initializeHeaders();
return super.getHeaderField(n);
}
public int getContentLength() {
initializeHeaders();
if (length > Integer.MAX_VALUE)
return -1;
return (int) length;
}
public long getContentLengthLong() {
initializeHeaders();
return length;
}
public String getHeaderFieldKey(int n) {
initializeHeaders();
return super.getHeaderFieldKey(n);
}
public MessageHeader getProperties() {
initializeHeaders();
return super.getProperties();
}
public long getLastModified() {
initializeHeaders();
return lastModified;
}
public synchronized InputStream getInputStream()
throws IOException {
int iconHeight;
int iconWidth;
connect();
if (is == null) {
if (isDirectory) {
FileNameMap map = java.net.URLConnection.getFileNameMap();
StringBuilder sb = new StringBuilder();
if (files == null) {
throw new FileNotFoundException(filename);
}
Collections.sort(files, Collator.getInstance());
for (int i = 0 ; i < files.size() ; i++) {
String fileName = files.get(i);
sb.append(fileName);
sb.append("\n");
}
// Put it into a (default) locale-specific byte-stream.
is = new ByteArrayInputStream(sb.toString().getBytes());
} else {
throw new FileNotFoundException(filename);
}
}
return is;
}
Permission permission;
/* since getOutputStream isn't supported, only read permission is
* relevant
*/
public Permission getPermission() throws IOException {
if (permission == null) {
String decodedPath = ParseUtil.decode(url.getPath());
if (File.separatorChar == '/') {
permission = new FilePermission(decodedPath, "read");
} else {
// decode could return /c:/x/y/z.
if (decodedPath.length() > 2 && decodedPath.charAt(0) == '/'
&& decodedPath.charAt(2) == ':') {
decodedPath = decodedPath.substring(1);
}
permission = new FilePermission(
decodedPath.replace('/', File.separatorChar), "read");
}
}
return permission;
}
}

View file

@ -0,0 +1,674 @@
/*
* Copyright (c) 1994, 2016, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* FTP stream opener.
*/
package sun.net.www.protocol.ftp;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.BufferedInputStream;
import java.io.FilterInputStream;
import java.io.FilterOutputStream;
import java.io.FileNotFoundException;
import java.net.URL;
import java.net.SocketPermission;
import java.net.UnknownHostException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.Proxy;
import java.net.ProxySelector;
import java.util.StringTokenizer;
import java.util.Iterator;
import java.security.Permission;
import java.util.Properties;
import sun.net.NetworkClient;
import sun.net.www.MessageHeader;
import sun.net.www.MeteredStream;
import sun.net.www.URLConnection;
import sun.net.www.protocol.http.HttpURLConnection;
import sun.net.ftp.FtpClient;
import sun.net.ftp.FtpProtocolException;
import sun.net.ProgressSource;
import sun.net.ProgressMonitor;
import sun.net.www.ParseUtil;
import sun.security.action.GetPropertyAction;
/**
* This class Opens an FTP input (or output) stream given a URL.
* It works as a one shot FTP transfer :
* <UL>
* <LI>Login</LI>
* <LI>Get (or Put) the file</LI>
* <LI>Disconnect</LI>
* </UL>
* You should not have to use it directly in most cases because all will be handled
* in a abstract layer. Here is an example of how to use the class:
* <pre>{@code
* URL url = new URL("ftp://ftp.sun.com/pub/test.txt");
* UrlConnection con = url.openConnection();
* InputStream is = con.getInputStream();
* ...
* is.close();
* }</pre>
*
* @see sun.net.ftp.FtpClient
*/
public class FtpURLConnection extends URLConnection {
// In case we have to use proxies, we use HttpURLConnection
HttpURLConnection http = null;
private Proxy instProxy;
InputStream is = null;
OutputStream os = null;
FtpClient ftp = null;
Permission permission;
String password;
String user;
String host;
String pathname;
String filename;
String fullpath;
int port;
static final int NONE = 0;
static final int ASCII = 1;
static final int BIN = 2;
static final int DIR = 3;
int type = NONE;
/* Redefine timeouts from java.net.URLConnection as we need -1 to mean
* not set. This is to ensure backward compatibility.
*/
private int connectTimeout = NetworkClient.DEFAULT_CONNECT_TIMEOUT;;
private int readTimeout = NetworkClient.DEFAULT_READ_TIMEOUT;;
/**
* For FTP URLs we need to have a special InputStream because we
* need to close 2 sockets after we're done with it :
* - The Data socket (for the file).
* - The command socket (FtpClient).
* Since that's the only class that needs to see that, it is an inner class.
*/
protected class FtpInputStream extends FilterInputStream {
FtpClient ftp;
FtpInputStream(FtpClient cl, InputStream fd) {
super(new BufferedInputStream(fd));
ftp = cl;
}
@Override
public void close() throws IOException {
super.close();
if (ftp != null) {
ftp.close();
}
}
}
/**
* For FTP URLs we need to have a special OutputStream because we
* need to close 2 sockets after we're done with it :
* - The Data socket (for the file).
* - The command socket (FtpClient).
* Since that's the only class that needs to see that, it is an inner class.
*/
protected class FtpOutputStream extends FilterOutputStream {
FtpClient ftp;
FtpOutputStream(FtpClient cl, OutputStream fd) {
super(fd);
ftp = cl;
}
@Override
public void close() throws IOException {
super.close();
if (ftp != null) {
ftp.close();
}
}
}
/**
* Creates an FtpURLConnection from a URL.
*
* @param url The {@code URL} to retrieve or store.
*/
public FtpURLConnection(URL url) {
this(url, null);
}
/**
* Same as FtpURLconnection(URL) with a per connection proxy specified
*/
FtpURLConnection(URL url, Proxy p) {
super(url);
instProxy = p;
host = url.getHost();
port = url.getPort();
String userInfo = url.getUserInfo();
if (userInfo != null) { // get the user and password
int delimiter = userInfo.indexOf(':');
if (delimiter == -1) {
user = ParseUtil.decode(userInfo);
password = null;
} else {
user = ParseUtil.decode(userInfo.substring(0, delimiter++));
password = ParseUtil.decode(userInfo.substring(delimiter));
}
}
}
private void setTimeouts() {
if (ftp != null) {
if (connectTimeout >= 0) {
ftp.setConnectTimeout(connectTimeout);
}
if (readTimeout >= 0) {
ftp.setReadTimeout(readTimeout);
}
}
}
/**
* Connects to the FTP server and logs in.
*
* @throws FtpLoginException if the login is unsuccessful
* @throws FtpProtocolException if an error occurs
* @throws UnknownHostException if trying to connect to an unknown host
*/
public synchronized void connect() throws IOException {
if (connected) {
return;
}
Proxy p = null;
if (instProxy == null) { // no per connection proxy specified
/**
* Do we have to use a proxy?
*/
ProxySelector sel = java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<ProxySelector>() {
public ProxySelector run() {
return ProxySelector.getDefault();
}
});
if (sel != null) {
URI uri = sun.net.www.ParseUtil.toURI(url);
Iterator<Proxy> it = sel.select(uri).iterator();
while (it.hasNext()) {
p = it.next();
if (p == null || p == Proxy.NO_PROXY ||
p.type() == Proxy.Type.SOCKS) {
break;
}
if (p.type() != Proxy.Type.HTTP ||
!(p.address() instanceof InetSocketAddress)) {
sel.connectFailed(uri, p.address(), new IOException("Wrong proxy type"));
continue;
}
// OK, we have an http proxy
InetSocketAddress paddr = (InetSocketAddress) p.address();
try {
http = new HttpURLConnection(url, p);
http.setDoInput(getDoInput());
http.setDoOutput(getDoOutput());
if (connectTimeout >= 0) {
http.setConnectTimeout(connectTimeout);
}
if (readTimeout >= 0) {
http.setReadTimeout(readTimeout);
}
http.connect();
connected = true;
return;
} catch (IOException ioe) {
sel.connectFailed(uri, paddr, ioe);
http = null;
}
}
}
} else { // per connection proxy specified
p = instProxy;
if (p.type() == Proxy.Type.HTTP) {
http = new HttpURLConnection(url, instProxy);
http.setDoInput(getDoInput());
http.setDoOutput(getDoOutput());
if (connectTimeout >= 0) {
http.setConnectTimeout(connectTimeout);
}
if (readTimeout >= 0) {
http.setReadTimeout(readTimeout);
}
http.connect();
connected = true;
return;
}
}
if (user == null) {
user = "anonymous";
Properties props = GetPropertyAction.privilegedGetProperties();
String vers = props.getProperty("java.version");
password = props.getProperty("ftp.protocol.user",
"Java" + vers + "@");
}
try {
ftp = FtpClient.create();
if (p != null) {
ftp.setProxy(p);
}
setTimeouts();
if (port != -1) {
ftp.connect(new InetSocketAddress(host, port));
} else {
ftp.connect(new InetSocketAddress(host, FtpClient.defaultPort()));
}
} catch (UnknownHostException e) {
// Maybe do something smart here, like use a proxy like iftp.
// Just keep throwing for now.
throw e;
} catch (FtpProtocolException fe) {
if (ftp != null) {
try {
ftp.close();
} catch (IOException ioe) {
fe.addSuppressed(ioe);
}
}
throw new IOException(fe);
}
try {
ftp.login(user, password == null ? null : password.toCharArray());
} catch (sun.net.ftp.FtpProtocolException e) {
ftp.close();
// Backward compatibility
throw new sun.net.ftp.FtpLoginException("Invalid username/password");
}
connected = true;
}
/*
* Decodes the path as per the RFC-1738 specifications.
*/
private void decodePath(String path) {
int i = path.indexOf(";type=");
if (i >= 0) {
String s1 = path.substring(i + 6, path.length());
if ("i".equalsIgnoreCase(s1)) {
type = BIN;
}
if ("a".equalsIgnoreCase(s1)) {
type = ASCII;
}
if ("d".equalsIgnoreCase(s1)) {
type = DIR;
}
path = path.substring(0, i);
}
if (path != null && path.length() > 1 &&
path.charAt(0) == '/') {
path = path.substring(1);
}
if (path == null || path.length() == 0) {
path = "./";
}
if (!path.endsWith("/")) {
i = path.lastIndexOf('/');
if (i > 0) {
filename = path.substring(i + 1, path.length());
filename = ParseUtil.decode(filename);
pathname = path.substring(0, i);
} else {
filename = ParseUtil.decode(path);
pathname = null;
}
} else {
pathname = path.substring(0, path.length() - 1);
filename = null;
}
if (pathname != null) {
fullpath = pathname + "/" + (filename != null ? filename : "");
} else {
fullpath = filename;
}
}
/*
* As part of RFC-1738 it is specified that the path should be
* interpreted as a series of FTP CWD commands.
* This is because, '/' is not necessarly the directory delimiter
* on every systems.
*/
private void cd(String path) throws FtpProtocolException, IOException {
if (path == null || path.isEmpty()) {
return;
}
if (path.indexOf('/') == -1) {
ftp.changeDirectory(ParseUtil.decode(path));
return;
}
StringTokenizer token = new StringTokenizer(path, "/");
while (token.hasMoreTokens()) {
ftp.changeDirectory(ParseUtil.decode(token.nextToken()));
}
}
/**
* Get the InputStream to retreive the remote file. It will issue the
* "get" (or "dir") command to the ftp server.
*
* @return the {@code InputStream} to the connection.
*
* @throws IOException if already opened for output
* @throws FtpProtocolException if errors occur during the transfert.
*/
@Override
public InputStream getInputStream() throws IOException {
if (!connected) {
connect();
}
if (http != null) {
return http.getInputStream();
}
if (os != null) {
throw new IOException("Already opened for output");
}
if (is != null) {
return is;
}
MessageHeader msgh = new MessageHeader();
boolean isAdir = false;
try {
decodePath(url.getPath());
if (filename == null || type == DIR) {
ftp.setAsciiType();
cd(pathname);
if (filename == null) {
is = new FtpInputStream(ftp, ftp.list(null));
} else {
is = new FtpInputStream(ftp, ftp.nameList(filename));
}
} else {
if (type == ASCII) {
ftp.setAsciiType();
} else {
ftp.setBinaryType();
}
cd(pathname);
is = new FtpInputStream(ftp, ftp.getFileStream(filename));
}
/* Try to get the size of the file in bytes. If that is
successful, then create a MeteredStream. */
try {
long l = ftp.getLastTransferSize();
msgh.add("content-length", Long.toString(l));
if (l > 0) {
// Wrap input stream with MeteredStream to ensure read() will always return -1
// at expected length.
// Check if URL should be metered
boolean meteredInput = ProgressMonitor.getDefault().shouldMeterInput(url, "GET");
ProgressSource pi = null;
if (meteredInput) {
pi = new ProgressSource(url, "GET", l);
pi.beginTracking();
}
is = new MeteredStream(is, pi, l);
}
} catch (Exception e) {
e.printStackTrace();
/* do nothing, since all we were doing was trying to
get the size in bytes of the file */
}
if (isAdir) {
msgh.add("content-type", "text/plain");
msgh.add("access-type", "directory");
} else {
msgh.add("access-type", "file");
String ftype = guessContentTypeFromName(fullpath);
if (ftype == null && is.markSupported()) {
ftype = guessContentTypeFromStream(is);
}
if (ftype != null) {
msgh.add("content-type", ftype);
}
}
} catch (FileNotFoundException e) {
try {
cd(fullpath);
/* if that worked, then make a directory listing
and build an html stream with all the files in
the directory */
ftp.setAsciiType();
is = new FtpInputStream(ftp, ftp.list(null));
msgh.add("content-type", "text/plain");
msgh.add("access-type", "directory");
} catch (IOException ex) {
FileNotFoundException fnfe = new FileNotFoundException(fullpath);
if (ftp != null) {
try {
ftp.close();
} catch (IOException ioe) {
fnfe.addSuppressed(ioe);
}
}
throw fnfe;
} catch (FtpProtocolException ex2) {
FileNotFoundException fnfe = new FileNotFoundException(fullpath);
if (ftp != null) {
try {
ftp.close();
} catch (IOException ioe) {
fnfe.addSuppressed(ioe);
}
}
throw fnfe;
}
} catch (FtpProtocolException ftpe) {
if (ftp != null) {
try {
ftp.close();
} catch (IOException ioe) {
ftpe.addSuppressed(ioe);
}
}
throw new IOException(ftpe);
}
setProperties(msgh);
return is;
}
/**
* Get the OutputStream to store the remote file. It will issue the
* "put" command to the ftp server.
*
* @return the {@code OutputStream} to the connection.
*
* @throws IOException if already opened for input or the URL
* points to a directory
* @throws FtpProtocolException if errors occur during the transfert.
*/
@Override
public OutputStream getOutputStream() throws IOException {
if (!connected) {
connect();
}
if (http != null) {
OutputStream out = http.getOutputStream();
// getInputStream() is neccessary to force a writeRequests()
// on the http client.
http.getInputStream();
return out;
}
if (is != null) {
throw new IOException("Already opened for input");
}
if (os != null) {
return os;
}
decodePath(url.getPath());
if (filename == null || filename.length() == 0) {
throw new IOException("illegal filename for a PUT");
}
try {
if (pathname != null) {
cd(pathname);
}
if (type == ASCII) {
ftp.setAsciiType();
} else {
ftp.setBinaryType();
}
os = new FtpOutputStream(ftp, ftp.putFileStream(filename, false));
} catch (FtpProtocolException e) {
throw new IOException(e);
}
return os;
}
String guessContentTypeFromFilename(String fname) {
return guessContentTypeFromName(fname);
}
/**
* Gets the {@code Permission} associated with the host and port.
*
* @return The {@code Permission} object.
*/
@Override
public Permission getPermission() {
if (permission == null) {
int urlport = url.getPort();
urlport = urlport < 0 ? FtpClient.defaultPort() : urlport;
String urlhost = this.host + ":" + urlport;
permission = new SocketPermission(urlhost, "connect");
}
return permission;
}
/**
* Sets the general request property. If a property with the key already
* exists, overwrite its value with the new value.
*
* @param key the keyword by which the request is known
* (e.g., "{@code accept}").
* @param value the value associated with it.
* @throws IllegalStateException if already connected
* @see #getRequestProperty(java.lang.String)
*/
@Override
public void setRequestProperty(String key, String value) {
super.setRequestProperty(key, value);
if ("type".equals(key)) {
if ("i".equalsIgnoreCase(value)) {
type = BIN;
} else if ("a".equalsIgnoreCase(value)) {
type = ASCII;
} else if ("d".equalsIgnoreCase(value)) {
type = DIR;
} else {
throw new IllegalArgumentException(
"Value of '" + key +
"' request property was '" + value +
"' when it must be either 'i', 'a' or 'd'");
}
}
}
/**
* Returns the value of the named general request property for this
* connection.
*
* @param key the keyword by which the request is known (e.g., "accept").
* @return the value of the named general request property for this
* connection.
* @throws IllegalStateException if already connected
* @see #setRequestProperty(java.lang.String, java.lang.String)
*/
@Override
public String getRequestProperty(String key) {
String value = super.getRequestProperty(key);
if (value == null) {
if ("type".equals(key)) {
value = (type == ASCII ? "a" : type == DIR ? "d" : "i");
}
}
return value;
}
@Override
public void setConnectTimeout(int timeout) {
if (timeout < 0) {
throw new IllegalArgumentException("timeouts can't be negative");
}
connectTimeout = timeout;
}
@Override
public int getConnectTimeout() {
return (connectTimeout < 0 ? 0 : connectTimeout);
}
@Override
public void setReadTimeout(int timeout) {
if (timeout < 0) {
throw new IllegalArgumentException("timeouts can't be negative");
}
readTimeout = timeout;
}
@Override
public int getReadTimeout() {
return readTimeout < 0 ? 0 : readTimeout;
}
}

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 1994, 2003, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*-
* FTP stream opener
*/
package sun.net.www.protocol.ftp;
import java.io.IOException;
import java.net.URL;
import java.net.Proxy;
import java.util.Map;
import java.util.HashMap;
import sun.net.ftp.FtpClient;
import sun.net.www.protocol.http.HttpURLConnection;
/** open an ftp connection given a URL */
public class Handler extends java.net.URLStreamHandler {
protected int getDefaultPort() {
return 21;
}
protected boolean equals(URL u1, URL u2) {
String userInfo1 = u1.getUserInfo();
String userInfo2 = u2.getUserInfo();
return super.equals(u1, u2) &&
(userInfo1 == null? userInfo2 == null: userInfo1.equals(userInfo2));
}
protected java.net.URLConnection openConnection(URL u)
throws IOException {
return openConnection(u, null);
}
protected java.net.URLConnection openConnection(URL u, Proxy p)
throws IOException {
return new FtpURLConnection(u, p);
}
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2003, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.http;
/**
* @author Michael McMahon
*
* Interface provided by internal http authentication cache.
* NB. This API will be replaced in a future release, and should
* not be made public.
*/
public interface AuthCache {
/**
* Put an entry in the cache. pkey is a string specified as follows:
*
* A:[B:]C:D:E[:F][;key=value] Between 4 and 6 fields separated by ":",
* and an optional semicolon-separated key=value list postfix,
* where the fields have the following meaning:
* A is "s" or "p" for server or proxy authentication respectively
* B is optional and is the {@link AuthScheme}, e.g. BASIC, DIGEST, NTLM, etc
* C is either "http" or "https"
* D is the hostname
* E is the port number
* F is optional and if present is the realm
*
* The semi-colon separated key=value list postfix can be used to
* provide additional contextual information, thus allowing
* to separate AuthCacheValue instances obtained from different
* contexts.
*
* Generally, two entries are created for each AuthCacheValue,
* one including the realm and one without the realm.
* Also, for some schemes (digest) multiple entries may be created
* with the same pkey, but with a different path value in
* the AuthCacheValue.
*/
public void put (String pkey, AuthCacheValue value);
/**
* Get an entry from the cache based on pkey as described above, but also
* using a pathname (skey) and the cache must return an entry
* if skey is a sub-path of the AuthCacheValue.path field.
*/
public AuthCacheValue get (String pkey, String skey);
/**
* remove the entry from the cache whose pkey is specified and
* whose path is equal to entry.path. If entry is null then
* all entries with the same pkey should be removed.
*/
public void remove (String pkey, AuthCacheValue entry);
}

View file

@ -0,0 +1,108 @@
/*
* Copyright (c) 2003, 2011, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.http;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.HashMap;
/**
* @author Michael McMahon
*/
public class AuthCacheImpl implements AuthCache {
HashMap<String,LinkedList<AuthCacheValue>> hashtable;
public AuthCacheImpl () {
hashtable = new HashMap<String,LinkedList<AuthCacheValue>>();
}
public void setMap (HashMap<String,LinkedList<AuthCacheValue>> map) {
hashtable = map;
}
// put a value in map according to primary key + secondary key which
// is the path field of AuthenticationInfo
public synchronized void put (String pkey, AuthCacheValue value) {
LinkedList<AuthCacheValue> list = hashtable.get (pkey);
String skey = value.getPath();
if (list == null) {
list = new LinkedList<AuthCacheValue>();
hashtable.put(pkey, list);
}
// Check if the path already exists or a super-set of it exists
ListIterator<AuthCacheValue> iter = list.listIterator();
while (iter.hasNext()) {
AuthenticationInfo inf = (AuthenticationInfo)iter.next();
if (inf.path == null || inf.path.startsWith (skey)) {
iter.remove ();
}
}
iter.add(value);
}
// get a value from map checking both primary
// and secondary (urlpath) key
public synchronized AuthCacheValue get (String pkey, String skey) {
AuthenticationInfo result = null;
LinkedList<AuthCacheValue> list = hashtable.get (pkey);
if (list == null || list.size() == 0) {
return null;
}
if (skey == null) {
// list should contain only one element
return (AuthenticationInfo)list.get (0);
}
ListIterator<AuthCacheValue> iter = list.listIterator();
while (iter.hasNext()) {
AuthenticationInfo inf = (AuthenticationInfo)iter.next();
if (skey.startsWith (inf.path)) {
return inf;
}
}
return null;
}
public synchronized void remove (String pkey, AuthCacheValue entry) {
LinkedList<AuthCacheValue> list = hashtable.get (pkey);
if (list == null) {
return;
}
if (entry == null) {
list.clear();
return;
}
ListIterator<AuthCacheValue> iter = list.listIterator ();
while (iter.hasNext()) {
AuthenticationInfo inf = (AuthenticationInfo)iter.next();
if (entry.equals(inf)) {
iter.remove ();
}
}
}
}

View file

@ -0,0 +1,100 @@
/*
* Copyright (c) 2003, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.http;
import java.io.Serializable;
import java.net.PasswordAuthentication;
/**
* AuthCacheValue: interface to minimize exposure to authentication cache
* for external users (ie. plugin)
*
* @author Michael McMahon
*/
public abstract class AuthCacheValue implements Serializable {
static final long serialVersionUID = 735249334068211611L;
public enum Type {
Proxy,
Server
};
/**
* Caches authentication info entered by user. See cacheKey()
*/
protected static AuthCache cache = new AuthCacheImpl();
public static void setAuthCache (AuthCache map) {
cache = map;
}
/* Package private ctor to prevent extension outside package */
AuthCacheValue() {}
/**
* Proxy or Server
*/
abstract Type getAuthType ();
/**
* Authentication scheme
*/
abstract AuthScheme getAuthScheme();
/**
* name of server/proxy
*/
abstract String getHost ();
/**
* portnumber of server/proxy
*/
abstract int getPort();
/**
* realm of authentication if known
*/
abstract String getRealm();
/**
* root path of realm or the request path if the root
* is not known yet.
*/
abstract String getPath();
/**
* returns http or https
*/
abstract String getProtocolScheme();
/**
* the credentials associated with this authentication
*/
abstract PasswordAuthentication credentials();
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2009, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.http;
/* Authentication schemes supported by the http implementation. New schemes, if
* supported, should be defined here.
*/
public enum AuthScheme {
BASIC,
DIGEST,
NTLM,
NEGOTIATE,
KERBEROS,
UNKNOWN;
}

View file

@ -0,0 +1,280 @@
/*
* Copyright (c) 2002, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.http;
import java.util.Collections;
import java.util.Iterator;
import java.util.HashMap;
import java.util.Set;
import sun.net.www.*;
import sun.security.action.GetPropertyAction;
/**
* This class is used to parse the information in WWW-Authenticate: and Proxy-Authenticate:
* headers. It searches among multiple header lines and within each header line
* for the best currently supported scheme. It can also return a HeaderParser
* containing the challenge data for that particular scheme.
*
* Some examples:
*
* WWW-Authenticate: Basic realm="foo" Digest realm="bar" NTLM
* Note the realm parameter must be associated with the particular scheme.
*
* or
*
* WWW-Authenticate: Basic realm="foo"
* WWW-Authenticate: Digest realm="foo",qop="auth",nonce="thisisanunlikelynonce"
* WWW-Authenticate: NTLM
*
* or
*
* WWW-Authenticate: Basic realm="foo"
* WWW-Authenticate: NTLM ASKAJK9893289889QWQIOIONMNMN
*
* The last example shows how NTLM breaks the rules of rfc2617 for the structure of
* the authentication header. This is the reason why the raw header field is used for ntlm.
*
* At present, the class chooses schemes in following order :
* 1. Negotiate (if supported)
* 2. Kerberos (if supported)
* 3. Digest
* 4. NTLM (if supported)
* 5. Basic
*
* This choice can be modified by setting a system property:
*
* -Dhttp.auth.preference="scheme"
*
* which in this case, specifies that "scheme" should be used as the auth scheme when offered
* disregarding the default prioritisation. If scheme is not offered, or explicitly
* disabled, by {@code disabledSchemes}, then the default priority is used.
*
* Attention: when http.auth.preference is set as SPNEGO or Kerberos, it's actually "Negotiate
* with SPNEGO" or "Negotiate with Kerberos", which means the user will prefer the Negotiate
* scheme with GSS/SPNEGO or GSS/Kerberos mechanism.
*
* This also means that the real "Kerberos" scheme can never be set as a preference.
*/
public class AuthenticationHeader {
MessageHeader rsp; // the response to be parsed
HeaderParser preferred;
String preferred_r; // raw Strings
private final HttpCallerInfo hci; // un-schemed, need check
// When set true, do not use Negotiate even if the response
// headers suggest so.
boolean dontUseNegotiate = false;
static String authPref=null;
public String toString() {
return "AuthenticationHeader: prefer " + preferred_r;
}
static {
authPref = GetPropertyAction.privilegedGetProperty("http.auth.preference");
// http.auth.preference can be set to SPNEGO or Kerberos.
// In fact they means "Negotiate with SPNEGO" and "Negotiate with
// Kerberos" separately, so here they are all translated into
// Negotiate. Read NegotiateAuthentication.java to see how they
// were used later.
if (authPref != null) {
authPref = authPref.toLowerCase();
if(authPref.equals("spnego") || authPref.equals("kerberos")) {
authPref = "negotiate";
}
}
}
String hdrname; // Name of the header to look for
/**
* Parses a set of authentication headers and chooses the preferred scheme
* that is supported for a given host.
*/
public AuthenticationHeader (String hdrname, MessageHeader response,
HttpCallerInfo hci, boolean dontUseNegotiate) {
this(hdrname, response, hci, dontUseNegotiate, Collections.emptySet());
}
/**
* Parses a set of authentication headers and chooses the preferred scheme
* that is supported for a given host.
*
* <p> The {@code disabledSchemes} parameter is a, possibly empty, set of
* authentication schemes that are disabled.
*/
public AuthenticationHeader(String hdrname,
MessageHeader response,
HttpCallerInfo hci,
boolean dontUseNegotiate,
Set<String> disabledSchemes) {
this.hci = hci;
this.dontUseNegotiate = dontUseNegotiate;
this.rsp = response;
this.hdrname = hdrname;
this.schemes = new HashMap<>();
parse(disabledSchemes);
}
public HttpCallerInfo getHttpCallerInfo() {
return hci;
}
/* we build up a map of scheme names mapped to SchemeMapValue objects */
static class SchemeMapValue {
SchemeMapValue (HeaderParser h, String r) {raw=r; parser=h;}
String raw;
HeaderParser parser;
}
HashMap<String, SchemeMapValue> schemes;
/* Iterate through each header line, and then within each line.
* If multiple entries exist for a particular scheme (unlikely)
* then the last one will be used. The
* preferred scheme that we support will be used.
*/
private void parse(Set<String> disabledSchemes) {
Iterator<String> iter = rsp.multiValueIterator(hdrname);
while (iter.hasNext()) {
String raw = iter.next();
// HeaderParser lower cases everything, so can be used case-insensitively
HeaderParser hp = new HeaderParser(raw);
Iterator<String> keys = hp.keys();
int i, lastSchemeIndex;
for (i=0, lastSchemeIndex = -1; keys.hasNext(); i++) {
keys.next();
if (hp.findValue(i) == null) { /* found a scheme name */
if (lastSchemeIndex != -1) {
HeaderParser hpn = hp.subsequence (lastSchemeIndex, i);
String scheme = hpn.findKey(0);
if (!disabledSchemes.contains(scheme))
schemes.put(scheme, new SchemeMapValue (hpn, raw));
}
lastSchemeIndex = i;
}
}
if (i > lastSchemeIndex) {
HeaderParser hpn = hp.subsequence (lastSchemeIndex, i);
String scheme = hpn.findKey(0);
if (!disabledSchemes.contains(scheme))
schemes.put(scheme, new SchemeMapValue (hpn, raw));
}
}
/* choose the best of them, the order is
* negotiate -> kerberos -> digest -> ntlm -> basic
*/
SchemeMapValue v = null;
if (authPref == null || (v=schemes.get (authPref)) == null) {
if(v == null && !dontUseNegotiate) {
SchemeMapValue tmp = schemes.get("negotiate");
if(tmp != null) {
if(hci == null || !NegotiateAuthentication.isSupported(new HttpCallerInfo(hci, "Negotiate"))) {
tmp = null;
}
v = tmp;
}
}
if(v == null && !dontUseNegotiate) {
SchemeMapValue tmp = schemes.get("kerberos");
if(tmp != null) {
// the Kerberos scheme is only observed in MS ISA Server. In
// fact i think it's a Kerberos-mechnism-only Negotiate.
// Since the Kerberos scheme is always accompanied with the
// Negotiate scheme, so it seems impossible to reach this
// line. Even if the user explicitly set http.auth.preference
// as Kerberos, it means Negotiate with Kerberos, and the code
// will still tried to use Negotiate at first.
//
// The only chance this line get executed is that the server
// only suggest the Kerberos scheme.
if(hci == null || !NegotiateAuthentication.isSupported(new HttpCallerInfo(hci, "Kerberos"))) {
tmp = null;
}
v = tmp;
}
}
if(v == null) {
if ((v=schemes.get ("digest")) == null) {
if (!NTLMAuthenticationProxy.supported
|| ((v=schemes.get("ntlm"))==null)) {
v = schemes.get ("basic");
}
}
}
} else { // authPref != null && it's found in reponses'
if (dontUseNegotiate && authPref.equals("negotiate")) {
v = null;
}
}
if (v != null) {
preferred = v.parser;
preferred_r = v.raw;
}
}
/**
* return a header parser containing the preferred authentication scheme (only).
* The preferred scheme is the strongest of the schemes proposed by the server.
* The returned HeaderParser will contain the relevant parameters for that scheme
*/
public HeaderParser headerParser() {
return preferred;
}
/**
* return the name of the preferred scheme
*/
public String scheme() {
if (preferred != null) {
return preferred.findKey(0);
} else {
return null;
}
}
/* return the raw header field for the preferred/chosen scheme */
public String raw () {
return preferred_r;
}
/**
* returns true is the header exists and contains a recognised scheme
*/
public boolean isPresent () {
return preferred != null;
}
}

View file

@ -0,0 +1,502 @@
/*
* Copyright (c) 1995, 2016, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.http;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.util.HashMap;
import java.util.Objects;
import sun.net.www.HeaderParser;
/**
* AuthenticationInfo: Encapsulate the information needed to
* authenticate a user to a server.
*
* @author Jon Payne
* @author Herb Jellinek
* @author Bill Foote
*/
// REMIND: It would be nice if this class understood about partial matching.
// If you're authorized for foo.com, chances are high you're also
// authorized for baz.foo.com.
// NB: When this gets implemented, be careful about the uncaching
// policy in HttpURLConnection. A failure on baz.foo.com shouldn't
// uncache foo.com!
public abstract class AuthenticationInfo extends AuthCacheValue implements Cloneable {
static final long serialVersionUID = -2588378268010453259L;
// Constants saying what kind of authroization this is. This determines
// the namespace in the hash table lookup.
public static final char SERVER_AUTHENTICATION = 's';
public static final char PROXY_AUTHENTICATION = 'p';
/**
* If true, then simultaneous authentication requests to the same realm/proxy
* are serialized, in order to avoid a user having to type the same username/passwords
* repeatedly, via the Authenticator. Default is false, which means that this
* behavior is switched off.
*/
static final boolean serializeAuth;
static {
serializeAuth = java.security.AccessController.doPrivileged(
new sun.security.action.GetBooleanAction(
"http.auth.serializeRequests")).booleanValue();
}
/* AuthCacheValue: */
protected transient PasswordAuthentication pw;
public PasswordAuthentication credentials() {
return pw;
}
public AuthCacheValue.Type getAuthType() {
return type == SERVER_AUTHENTICATION ?
AuthCacheValue.Type.Server:
AuthCacheValue.Type.Proxy;
}
AuthScheme getAuthScheme() {
return authScheme;
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
public String getRealm() {
return realm;
}
public String getPath() {
return path;
}
public String getProtocolScheme() {
return protocol;
}
/**
* Whether we should cache this instance in the AuthCache.
* This method returns {@code true} by default.
* Subclasses may override this method to add
* additional restrictions.
* @return {@code true} by default.
*/
protected boolean useAuthCache() {
return true;
}
/**
* requests is used to ensure that interaction with the
* Authenticator for a particular realm is single threaded.
* ie. if multiple threads need to get credentials from the user
* at the same time, then all but the first will block until
* the first completes its authentication.
*/
private static HashMap<String,Thread> requests = new HashMap<>();
/* check if a request for this destination is in progress
* return false immediately if not. Otherwise block until
* request is finished and return true
*/
private static boolean requestIsInProgress (String key) {
if (!serializeAuth) {
/* behavior is disabled. Revert to concurrent requests */
return false;
}
synchronized (requests) {
Thread t, c;
c = Thread.currentThread();
if ((t = requests.get(key)) == null) {
requests.put (key, c);
return false;
}
if (t == c) {
return false;
}
while (requests.containsKey(key)) {
try {
requests.wait ();
} catch (InterruptedException e) {}
}
}
/* entry may be in cache now. */
return true;
}
/* signal completion of an authentication (whether it succeeded or not)
* so that other threads can continue.
*/
private static void requestCompleted (String key) {
synchronized (requests) {
Thread thread = requests.get(key);
if (thread != null && thread == Thread.currentThread()) {
boolean waspresent = requests.remove(key) != null;
assert waspresent;
}
requests.notifyAll();
}
}
//public String toString () {
//return ("{"+type+":"+authScheme+":"+protocol+":"+host+":"+port+":"+realm+":"+path+"}");
//}
// REMIND: This cache just grows forever. We should put in a bounded
// cache, or maybe something using WeakRef's.
/** The type (server/proxy) of authentication this is. Used for key lookup */
char type;
/** The authentication scheme (basic/digest). Also used for key lookup */
AuthScheme authScheme;
/** The protocol/scheme (i.e. http or https ). Need to keep the caches
* logically separate for the two protocols. This field is only used
* when constructed with a URL (the normal case for server authentication)
* For proxy authentication the protocol is not relevant.
*/
String protocol;
/** The host we're authenticating against. */
String host;
/** The port on the host we're authenticating against. */
int port;
/** The realm we're authenticating against. */
String realm;
/** The shortest path from the URL we authenticated against. */
String path;
/**
* A key identifying the authenticator from which the credentials
* were obtained.
* {@link AuthenticatorKeys#DEFAULT} identifies the {@linkplain
* java.net.Authenticator#setDefault(java.net.Authenticator) default}
* authenticator.
*/
String authenticatorKey;
/** Use this constructor only for proxy entries */
public AuthenticationInfo(char type, AuthScheme authScheme, String host,
int port, String realm, String authenticatorKey) {
this.type = type;
this.authScheme = authScheme;
this.protocol = "";
this.host = host.toLowerCase();
this.port = port;
this.realm = realm;
this.path = null;
this.authenticatorKey = Objects.requireNonNull(authenticatorKey);
}
public Object clone() {
try {
return super.clone ();
} catch (CloneNotSupportedException e) {
// Cannot happen because Cloneable implemented by AuthenticationInfo
return null;
}
}
/*
* Constructor used to limit the authorization to the path within
* the URL. Use this constructor for origin server entries.
*/
public AuthenticationInfo(char type, AuthScheme authScheme, URL url, String realm,
String authenticatorKey) {
this.type = type;
this.authScheme = authScheme;
this.protocol = url.getProtocol().toLowerCase();
this.host = url.getHost().toLowerCase();
this.port = url.getPort();
if (this.port == -1) {
this.port = url.getDefaultPort();
}
this.realm = realm;
String urlPath = url.getPath();
if (urlPath.length() == 0)
this.path = urlPath;
else {
this.path = reducePath (urlPath);
}
this.authenticatorKey = Objects.requireNonNull(authenticatorKey);
}
/**
* The {@linkplain java.net.Authenticator#getKey(java.net.Authenticator) key}
* of the authenticator that was used to obtain the credentials.
* @return The authenticator's key.
*/
public final String getAuthenticatorKey() {
return authenticatorKey;
}
/*
* reduce the path to the root of where we think the
* authorization begins. This could get shorter as
* the url is traversed up following a successful challenge.
*/
static String reducePath (String urlPath) {
int sepIndex = urlPath.lastIndexOf('/');
int targetSuffixIndex = urlPath.lastIndexOf('.');
if (sepIndex != -1)
if (sepIndex < targetSuffixIndex)
return urlPath.substring(0, sepIndex+1);
else
return urlPath;
else
return urlPath;
}
/**
* Returns info for the URL, for an HTTP server auth. Used when we
* don't yet know the realm
* (i.e. when we're preemptively setting the auth).
*/
static AuthenticationInfo getServerAuth(URL url, String authenticatorKey) {
int port = url.getPort();
if (port == -1) {
port = url.getDefaultPort();
}
String key = SERVER_AUTHENTICATION + ":" + url.getProtocol().toLowerCase()
+ ":" + url.getHost().toLowerCase() + ":" + port
+ ";auth=" + authenticatorKey;
return getAuth(key, url);
}
/**
* Returns info for the URL, for an HTTP server auth. Used when we
* do know the realm (i.e. when we're responding to a challenge).
* In this case we do not use the path because the protection space
* is identified by the host:port:realm only
*/
static String getServerAuthKey(URL url, String realm, AuthScheme scheme,
String authenticatorKey) {
int port = url.getPort();
if (port == -1) {
port = url.getDefaultPort();
}
String key = SERVER_AUTHENTICATION + ":" + scheme + ":"
+ url.getProtocol().toLowerCase()
+ ":" + url.getHost().toLowerCase()
+ ":" + port + ":" + realm
+ ";auth=" + authenticatorKey;
return key;
}
static AuthenticationInfo getServerAuth(String key) {
AuthenticationInfo cached = getAuth(key, null);
if ((cached == null) && requestIsInProgress (key)) {
/* check the cache again, it might contain an entry */
cached = getAuth(key, null);
}
return cached;
}
/**
* Return the AuthenticationInfo object from the cache if it's path is
* a substring of the supplied URLs path.
*/
static AuthenticationInfo getAuth(String key, URL url) {
if (url == null) {
return (AuthenticationInfo)cache.get (key, null);
} else {
return (AuthenticationInfo)cache.get (key, url.getPath());
}
}
/**
* Returns a firewall authentication, for the given host/port. Used
* for preemptive header-setting. Note, the protocol field is always
* blank for proxies.
*/
static AuthenticationInfo getProxyAuth(String host, int port,
String authenticatorKey) {
String key = PROXY_AUTHENTICATION + "::" + host.toLowerCase() + ":" + port
+ ";auth=" + authenticatorKey;
AuthenticationInfo result = (AuthenticationInfo) cache.get(key, null);
return result;
}
/**
* Returns a firewall authentication, for the given host/port and realm.
* Used in response to a challenge. Note, the protocol field is always
* blank for proxies.
*/
static String getProxyAuthKey(String host, int port, String realm,
AuthScheme scheme, String authenticatorKey) {
String key = PROXY_AUTHENTICATION + ":" + scheme
+ "::" + host.toLowerCase()
+ ":" + port + ":" + realm
+ ";auth=" + authenticatorKey;
return key;
}
static AuthenticationInfo getProxyAuth(String key) {
AuthenticationInfo cached = (AuthenticationInfo) cache.get(key, null);
if ((cached == null) && requestIsInProgress (key)) {
/* check the cache again, it might contain an entry */
cached = (AuthenticationInfo) cache.get(key, null);
}
return cached;
}
/**
* Add this authentication to the cache
*/
void addToCache() {
String key = cacheKey(true);
if (useAuthCache()) {
cache.put(key, this);
if (supportsPreemptiveAuthorization()) {
cache.put(cacheKey(false), this);
}
}
endAuthRequest(key);
}
static void endAuthRequest (String key) {
if (!serializeAuth) {
return;
}
synchronized (requests) {
requestCompleted(key);
}
}
/**
* Remove this authentication from the cache
*/
void removeFromCache() {
cache.remove(cacheKey(true), this);
if (supportsPreemptiveAuthorization()) {
cache.remove(cacheKey(false), this);
}
}
/**
* @return true if this authentication supports preemptive authorization
*/
public abstract boolean supportsPreemptiveAuthorization();
/**
* @return the name of the HTTP header this authentication wants set.
* This is used for preemptive authorization.
*/
public String getHeaderName() {
if (type == SERVER_AUTHENTICATION) {
return "Authorization";
} else {
return "Proxy-authorization";
}
}
/**
* Calculates and returns the authentication header value based
* on the stored authentication parameters. If the calculation does not depend
* on the URL or the request method then these parameters are ignored.
* @param url The URL
* @param method The request method
* @return the value of the HTTP header this authentication wants set.
* Used for preemptive authorization.
*/
public abstract String getHeaderValue(URL url, String method);
/**
* Set header(s) on the given connection. Subclasses must override
* This will only be called for
* definitive (i.e. non-preemptive) authorization.
* @param conn The connection to apply the header(s) to
* @param p A source of header values for this connection, if needed.
* @param raw The raw header field (if needed)
* @return true if all goes well, false if no headers were set.
*/
public abstract boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw);
/**
* Check if the header indicates that the current auth. parameters are stale.
* If so, then replace the relevant field with the new value
* and return true. Otherwise return false.
* returning true means the request can be retried with the same userid/password
* returning false means we have to go back to the user to ask for a new
* username password.
*/
public abstract boolean isAuthorizationStale (String header);
/**
* Give a key for hash table lookups.
* @param includeRealm if you want the realm considered. Preemptively
* setting an authorization is done before the realm is known.
*/
String cacheKey(boolean includeRealm) {
// This must be kept in sync with the getXXXAuth() methods in this
// class.
String authenticatorKey = getAuthenticatorKey();
if (includeRealm) {
return type + ":" + authScheme + ":" + protocol + ":"
+ host + ":" + port + ":" + realm
+ ";auth=" + authenticatorKey;
} else {
return type + ":" + protocol + ":" + host + ":" + port
+ ";auth=" + authenticatorKey;
}
}
String s1, s2; /* used for serialization of pw */
private synchronized void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException
{
s.defaultReadObject ();
pw = new PasswordAuthentication (s1, s2.toCharArray());
s1 = null; s2= null;
if (authenticatorKey == null) {
authenticatorKey = AuthenticatorKeys.DEFAULT;
}
}
private synchronized void writeObject(java.io.ObjectOutputStream s)
throws IOException
{
Objects.requireNonNull(authenticatorKey);
s1 = pw.getUserName();
s2 = new String (pw.getPassword());
s.defaultWriteObject ();
}
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2016, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.http;
import java.net.Authenticator;
import java.util.concurrent.atomic.AtomicLong;
/**
* A class used to tie a key to an authenticator instance.
*/
public final class AuthenticatorKeys {
private AuthenticatorKeys() {
throw new InternalError("Trying to instantiate static class");
}
public static final String DEFAULT = "default";
private static final AtomicLong IDS = new AtomicLong();
public static String computeKey(Authenticator a) {
return System.identityHashCode(a) + "-" + IDS.incrementAndGet()
+ "@" + a.getClass().getName();
}
/**
* Returns a key for the given authenticator.
*
* @param authenticator The authenticator; {@code null} should be
* passed when the {@linkplain
* Authenticator#setDefault(java.net.Authenticator) default}
* authenticator is meant.
* @return A key for the given authenticator, {@link #DEFAULT} for
* {@code null}.
*/
public static String getKey(Authenticator authenticator) {
if (authenticator == null) {
return DEFAULT;
}
return authenticatorKeyAccess.getKey(authenticator);
}
@FunctionalInterface
public interface AuthenticatorKeyAccess {
public String getKey(Authenticator a);
}
private static AuthenticatorKeyAccess authenticatorKeyAccess;
public static void setAuthenticatorKeyAccess(AuthenticatorKeyAccess access) {
if (authenticatorKeyAccess == null && access != null) {
authenticatorKeyAccess = access;
}
}
}

View file

@ -0,0 +1,212 @@
/*
* Copyright (c) 1997, 2016, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.http;
import java.net.URL;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.PasswordAuthentication;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Base64;
import java.util.Objects;
import sun.net.www.HeaderParser;
/**
* BasicAuthentication: Encapsulate an http server authentication using
* the "basic" scheme.
*
* @author Bill Foote
*/
class BasicAuthentication extends AuthenticationInfo {
private static final long serialVersionUID = 100L;
/** The authentication string for this host, port, and realm. This is
a simple BASE64 encoding of "login:password". */
String auth;
/**
* Create a BasicAuthentication
*/
public BasicAuthentication(boolean isProxy, String host, int port,
String realm, PasswordAuthentication pw,
String authenticatorKey) {
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.BASIC, host, port, realm,
Objects.requireNonNull(authenticatorKey));
String plain = pw.getUserName() + ":";
byte[] nameBytes = null;
try {
nameBytes = plain.getBytes("ISO-8859-1");
} catch (java.io.UnsupportedEncodingException uee) {
assert false;
}
// get password bytes
char[] passwd = pw.getPassword();
byte[] passwdBytes = new byte[passwd.length];
for (int i=0; i<passwd.length; i++)
passwdBytes[i] = (byte)passwd[i];
// concatenate user name and password bytes and encode them
byte[] concat = new byte[nameBytes.length + passwdBytes.length];
System.arraycopy(nameBytes, 0, concat, 0, nameBytes.length);
System.arraycopy(passwdBytes, 0, concat, nameBytes.length,
passwdBytes.length);
this.auth = "Basic " + Base64.getEncoder().encodeToString(concat);
this.pw = pw;
}
/**
* Create a BasicAuthentication
*/
public BasicAuthentication(boolean isProxy, String host, int port,
String realm, String auth,
String authenticatorKey) {
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.BASIC, host, port, realm,
Objects.requireNonNull(authenticatorKey));
this.auth = "Basic " + auth;
}
/**
* Create a BasicAuthentication
*/
public BasicAuthentication(boolean isProxy, URL url, String realm,
PasswordAuthentication pw,
String authenticatorKey) {
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.BASIC, url, realm,
Objects.requireNonNull(authenticatorKey));
String plain = pw.getUserName() + ":";
byte[] nameBytes = null;
try {
nameBytes = plain.getBytes("ISO-8859-1");
} catch (java.io.UnsupportedEncodingException uee) {
assert false;
}
// get password bytes
char[] passwd = pw.getPassword();
byte[] passwdBytes = new byte[passwd.length];
for (int i=0; i<passwd.length; i++)
passwdBytes[i] = (byte)passwd[i];
// concatenate user name and password bytes and encode them
byte[] concat = new byte[nameBytes.length + passwdBytes.length];
System.arraycopy(nameBytes, 0, concat, 0, nameBytes.length);
System.arraycopy(passwdBytes, 0, concat, nameBytes.length,
passwdBytes.length);
this.auth = "Basic " + Base64.getEncoder().encodeToString(concat);
this.pw = pw;
}
/**
* Create a BasicAuthentication
*/
public BasicAuthentication(boolean isProxy, URL url, String realm,
String auth, String authenticatorKey) {
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.BASIC, url, realm,
Objects.requireNonNull(authenticatorKey));
this.auth = "Basic " + auth;
}
/**
* @return true if this authentication supports preemptive authorization
*/
@Override
public boolean supportsPreemptiveAuthorization() {
return true;
}
/**
* Set header(s) on the given connection. This will only be called for
* definitive (i.e. non-preemptive) authorization.
* @param conn The connection to apply the header(s) to
* @param p A source of header values for this connection, if needed.
* @param raw The raw header values for this connection, if needed.
* @return true if all goes well, false if no headers were set.
*/
@Override
public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
conn.setAuthenticationProperty(getHeaderName(), getHeaderValue(null,null));
return true;
}
/**
* @return the value of the HTTP header this authentication wants set
*/
@Override
public String getHeaderValue(URL url, String method) {
/* For Basic the authorization string does not depend on the request URL
* or the request method
*/
return auth;
}
/**
* For Basic Authentication, the security parameters can never be stale.
* In other words there is no possibility to reuse the credentials.
* They are always either valid or invalid.
*/
@Override
public boolean isAuthorizationStale (String header) {
return false;
}
/**
* @return the common root path between npath and path.
* This is used to detect when we have an authentication for two
* paths and the root of th authentication space is the common root.
*/
static String getRootPath(String npath, String opath) {
int index = 0;
int toindex;
/* Must normalize so we don't get confused by ../ and ./ segments */
try {
npath = new URI (npath).normalize().getPath();
opath = new URI (opath).normalize().getPath();
} catch (URISyntaxException e) {
/* ignore error and use the old value */
}
while (index < opath.length()) {
toindex = opath.indexOf('/', index+1);
if (toindex != -1 && opath.regionMatches(0, npath, 0, toindex+1))
index = toindex;
else
return opath.substring(0, index+1);
}
/*should not reach here. If we do simply return npath*/
return npath;
}
}

View file

@ -0,0 +1,546 @@
/*
* Copyright (c) 1997, 2016, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.http;
import java.io.*;
import java.net.URL;
import java.net.ProtocolException;
import java.net.PasswordAuthentication;
import java.util.Arrays;
import java.util.Random;
import sun.net.www.HeaderParser;
import sun.net.NetProperties;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedAction;
import java.security.AccessController;
import java.util.Objects;
import static sun.net.www.protocol.http.HttpURLConnection.HTTP_CONNECT;
/**
* DigestAuthentication: Encapsulate an http server authentication using
* the "Digest" scheme, as described in RFC2069 and updated in RFC2617
*
* @author Bill Foote
*/
class DigestAuthentication extends AuthenticationInfo {
private static final long serialVersionUID = 100L;
private String authMethod;
private static final String compatPropName = "http.auth.digest." +
"quoteParameters";
// true if http.auth.digest.quoteParameters Net property is true
private static final boolean delimCompatFlag;
static {
Boolean b = AccessController.doPrivileged(
new PrivilegedAction<>() {
public Boolean run() {
return NetProperties.getBoolean(compatPropName);
}
}
);
delimCompatFlag = (b == null) ? false : b.booleanValue();
}
// Authentication parameters defined in RFC2617.
// One instance of these may be shared among several DigestAuthentication
// instances as a result of a single authorization (for multiple domains)
static class Parameters implements java.io.Serializable {
private static final long serialVersionUID = -3584543755194526252L;
private boolean serverQop; // server proposed qop=auth
private String opaque;
private String cnonce;
private String nonce;
private String algorithm;
private int NCcount=0;
// The H(A1) string used for MD5-sess
private String cachedHA1;
// Force the HA1 value to be recalculated because the nonce has changed
private boolean redoCachedHA1 = true;
private static final int cnonceRepeat = 5;
private static final int cnoncelen = 40; /* number of characters in cnonce */
private static Random random;
static {
random = new Random();
}
Parameters () {
serverQop = false;
opaque = null;
algorithm = null;
cachedHA1 = null;
nonce = null;
setNewCnonce();
}
boolean authQop () {
return serverQop;
}
synchronized void incrementNC() {
NCcount ++;
}
synchronized int getNCCount () {
return NCcount;
}
int cnonce_count = 0;
/* each call increments the counter */
synchronized String getCnonce () {
if (cnonce_count >= cnonceRepeat) {
setNewCnonce();
}
cnonce_count++;
return cnonce;
}
synchronized void setNewCnonce () {
byte bb[] = new byte [cnoncelen/2];
char cc[] = new char [cnoncelen];
random.nextBytes (bb);
for (int i=0; i<(cnoncelen/2); i++) {
int x = bb[i] + 128;
cc[i*2]= (char) ('A'+ x/16);
cc[i*2+1]= (char) ('A'+ x%16);
}
cnonce = new String (cc, 0, cnoncelen);
cnonce_count = 0;
redoCachedHA1 = true;
}
synchronized void setQop (String qop) {
if (qop != null) {
String items[] = qop.split(",");
for (String item : items) {
if ("auth".equalsIgnoreCase(item.trim())) {
serverQop = true;
return;
}
}
}
serverQop = false;
}
synchronized String getOpaque () { return opaque;}
synchronized void setOpaque (String s) { opaque=s;}
synchronized String getNonce () { return nonce;}
synchronized void setNonce (String s) {
if (nonce == null || !s.equals(nonce)) {
nonce=s;
NCcount = 0;
redoCachedHA1 = true;
}
}
synchronized String getCachedHA1 () {
if (redoCachedHA1) {
return null;
} else {
return cachedHA1;
}
}
synchronized void setCachedHA1 (String s) {
cachedHA1=s;
redoCachedHA1=false;
}
synchronized String getAlgorithm () { return algorithm;}
synchronized void setAlgorithm (String s) { algorithm=s;}
}
Parameters params;
/**
* Create a DigestAuthentication
*/
public DigestAuthentication(boolean isProxy, URL url, String realm,
String authMethod, PasswordAuthentication pw,
Parameters params, String authenticatorKey) {
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.DIGEST,
url,
realm,
Objects.requireNonNull(authenticatorKey));
this.authMethod = authMethod;
this.pw = pw;
this.params = params;
}
public DigestAuthentication(boolean isProxy, String host, int port, String realm,
String authMethod, PasswordAuthentication pw,
Parameters params, String authenticatorKey) {
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.DIGEST,
host,
port,
realm,
Objects.requireNonNull(authenticatorKey));
this.authMethod = authMethod;
this.pw = pw;
this.params = params;
}
/**
* @return true if this authentication supports preemptive authorization
*/
@Override
public boolean supportsPreemptiveAuthorization() {
return true;
}
/**
* Recalculates the request-digest and returns it.
*
* <P> Used in the common case where the requestURI is simply the
* abs_path.
*
* @param url
* the URL
*
* @param method
* the HTTP method
*
* @return the value of the HTTP header this authentication wants set
*/
@Override
public String getHeaderValue(URL url, String method) {
return getHeaderValueImpl(url.getFile(), method);
}
/**
* Recalculates the request-digest and returns it.
*
* <P> Used when the requestURI is not the abs_path. The exact
* requestURI can be passed as a String.
*
* @param requestURI
* the Request-URI from the HTTP request line
*
* @param method
* the HTTP method
*
* @return the value of the HTTP header this authentication wants set
*/
String getHeaderValue(String requestURI, String method) {
return getHeaderValueImpl(requestURI, method);
}
/**
* Check if the header indicates that the current auth. parameters are stale.
* If so, then replace the relevant field with the new value
* and return true. Otherwise return false.
* returning true means the request can be retried with the same userid/password
* returning false means we have to go back to the user to ask for a new
* username password.
*/
@Override
public boolean isAuthorizationStale (String header) {
HeaderParser p = new HeaderParser (header);
String s = p.findValue ("stale");
if (s == null || !s.equals("true"))
return false;
String newNonce = p.findValue ("nonce");
if (newNonce == null || "".equals(newNonce)) {
return false;
}
params.setNonce (newNonce);
return true;
}
/**
* Set header(s) on the given connection.
* @param conn The connection to apply the header(s) to
* @param p A source of header values for this connection, if needed.
* @param raw Raw header values for this connection, if needed.
* @return true if all goes well, false if no headers were set.
*/
@Override
public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
params.setNonce (p.findValue("nonce"));
params.setOpaque (p.findValue("opaque"));
params.setQop (p.findValue("qop"));
String uri="";
String method;
if (type == PROXY_AUTHENTICATION &&
conn.tunnelState() == HttpURLConnection.TunnelState.SETUP) {
uri = HttpURLConnection.connectRequestURI(conn.getURL());
method = HTTP_CONNECT;
} else {
try {
uri = conn.getRequestURI();
} catch (IOException e) {}
method = conn.getMethod();
}
if (params.nonce == null || authMethod == null || pw == null || realm == null) {
return false;
}
if (authMethod.length() >= 1) {
// Method seems to get converted to all lower case elsewhere.
// It really does need to start with an upper case letter
// here.
authMethod = Character.toUpperCase(authMethod.charAt(0))
+ authMethod.substring(1).toLowerCase();
}
String algorithm = p.findValue("algorithm");
if (algorithm == null || "".equals(algorithm)) {
algorithm = "MD5"; // The default, accoriding to rfc2069
}
params.setAlgorithm (algorithm);
// If authQop is true, then the server is doing RFC2617 and
// has offered qop=auth. We do not support any other modes
// and if auth is not offered we fallback to the RFC2069 behavior
if (params.authQop()) {
params.setNewCnonce();
}
String value = getHeaderValueImpl (uri, method);
if (value != null) {
conn.setAuthenticationProperty(getHeaderName(), value);
return true;
} else {
return false;
}
}
/* Calculate the Authorization header field given the request URI
* and based on the authorization information in params
*/
private String getHeaderValueImpl (String uri, String method) {
String response;
char[] passwd = pw.getPassword();
boolean qop = params.authQop();
String opaque = params.getOpaque();
String cnonce = params.getCnonce ();
String nonce = params.getNonce ();
String algorithm = params.getAlgorithm ();
params.incrementNC ();
int nccount = params.getNCCount ();
String ncstring=null;
if (nccount != -1) {
ncstring = Integer.toHexString (nccount).toLowerCase();
int len = ncstring.length();
if (len < 8)
ncstring = zeroPad [len] + ncstring;
}
try {
response = computeDigest(true, pw.getUserName(),passwd,realm,
method, uri, nonce, cnonce, ncstring);
} catch (NoSuchAlgorithmException ex) {
return null;
}
String ncfield = "\"";
if (qop) {
ncfield = "\", nc=" + ncstring;
}
String algoS, qopS;
if (delimCompatFlag) {
// Put quotes around these String value parameters
algoS = ", algorithm=\"" + algorithm + "\"";
qopS = ", qop=\"auth\"";
} else {
// Don't put quotes around them, per the RFC
algoS = ", algorithm=" + algorithm;
qopS = ", qop=auth";
}
String value = authMethod
+ " username=\"" + pw.getUserName()
+ "\", realm=\"" + realm
+ "\", nonce=\"" + nonce
+ ncfield
+ ", uri=\"" + uri
+ "\", response=\"" + response + "\""
+ algoS;
if (opaque != null) {
value += ", opaque=\"" + opaque + "\"";
}
if (cnonce != null) {
value += ", cnonce=\"" + cnonce + "\"";
}
if (qop) {
value += qopS;
}
return value;
}
public void checkResponse (String header, String method, URL url)
throws IOException {
checkResponse (header, method, url.getFile());
}
public void checkResponse (String header, String method, String uri)
throws IOException {
char[] passwd = pw.getPassword();
String username = pw.getUserName();
boolean qop = params.authQop();
String opaque = params.getOpaque();
String cnonce = params.cnonce;
String nonce = params.getNonce ();
String algorithm = params.getAlgorithm ();
int nccount = params.getNCCount ();
String ncstring=null;
if (header == null) {
throw new ProtocolException ("No authentication information in response");
}
if (nccount != -1) {
ncstring = Integer.toHexString (nccount).toUpperCase();
int len = ncstring.length();
if (len < 8)
ncstring = zeroPad [len] + ncstring;
}
try {
String expected = computeDigest(false, username,passwd,realm,
method, uri, nonce, cnonce, ncstring);
HeaderParser p = new HeaderParser (header);
String rspauth = p.findValue ("rspauth");
if (rspauth == null) {
throw new ProtocolException ("No digest in response");
}
if (!rspauth.equals (expected)) {
throw new ProtocolException ("Response digest invalid");
}
/* Check if there is a nextnonce field */
String nextnonce = p.findValue ("nextnonce");
if (nextnonce != null && ! "".equals(nextnonce)) {
params.setNonce (nextnonce);
}
} catch (NoSuchAlgorithmException ex) {
throw new ProtocolException ("Unsupported algorithm in response");
}
}
private String computeDigest(
boolean isRequest, String userName, char[] password,
String realm, String connMethod,
String requestURI, String nonceString,
String cnonce, String ncValue
) throws NoSuchAlgorithmException
{
String A1, HashA1;
String algorithm = params.getAlgorithm ();
boolean md5sess = algorithm.equalsIgnoreCase ("MD5-sess");
MessageDigest md = MessageDigest.getInstance(md5sess?"MD5":algorithm);
if (md5sess) {
if ((HashA1 = params.getCachedHA1 ()) == null) {
String s = userName + ":" + realm + ":";
String s1 = encode (s, password, md);
A1 = s1 + ":" + nonceString + ":" + cnonce;
HashA1 = encode(A1, null, md);
params.setCachedHA1 (HashA1);
}
} else {
A1 = userName + ":" + realm + ":";
HashA1 = encode(A1, password, md);
}
String A2;
if (isRequest) {
A2 = connMethod + ":" + requestURI;
} else {
A2 = ":" + requestURI;
}
String HashA2 = encode(A2, null, md);
String combo, finalHash;
if (params.authQop()) { /* RRC2617 when qop=auth */
combo = HashA1+ ":" + nonceString + ":" + ncValue + ":" +
cnonce + ":auth:" +HashA2;
} else { /* for compatibility with RFC2069 */
combo = HashA1 + ":" +
nonceString + ":" +
HashA2;
}
finalHash = encode(combo, null, md);
return finalHash;
}
private static final char charArray[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
private static final String zeroPad[] = {
// 0 1 2 3 4 5 6 7
"00000000", "0000000", "000000", "00000", "0000", "000", "00", "0"
};
private String encode(String src, char[] passwd, MessageDigest md) {
try {
md.update(src.getBytes("ISO-8859-1"));
} catch (java.io.UnsupportedEncodingException uee) {
assert false;
}
if (passwd != null) {
byte[] passwdBytes = new byte[passwd.length];
for (int i=0; i<passwd.length; i++)
passwdBytes[i] = (byte)passwd[i];
md.update(passwdBytes);
Arrays.fill(passwdBytes, (byte)0x00);
}
byte[] digest = md.digest();
StringBuilder res = new StringBuilder(digest.length * 2);
for (int i = 0; i < digest.length; i++) {
int hashchar = ((digest[i] >>> 4) & 0xf);
res.append(charArray[hashchar]);
hashchar = (digest[i] & 0xf);
res.append(charArray[hashchar]);
}
return res.toString();
}
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 1994, 2003, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*-
* HTTP stream opener
*/
package sun.net.www.protocol.http;
import java.io.IOException;
import java.net.URL;
import java.net.Proxy;
/** open an http input stream given a URL */
public class Handler extends java.net.URLStreamHandler {
protected String proxy;
protected int proxyPort;
protected int getDefaultPort() {
return 80;
}
public Handler () {
proxy = null;
proxyPort = -1;
}
public Handler (String proxy, int port) {
this.proxy = proxy;
this.proxyPort = port;
}
protected java.net.URLConnection openConnection(URL u)
throws IOException {
return openConnection(u, (Proxy)null);
}
protected java.net.URLConnection openConnection(URL u, Proxy p)
throws IOException {
return new HttpURLConnection(u, p, this);
}
}

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 1996, 2004, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.http;
import java.net.URL;
/**
* An interface for all objects that implement HTTP authentication.
* See the HTTP spec for details on how this works in general.
* A single class or object can implement an arbitrary number of
* authentication schemes.
*
* @author David Brown
*
* @deprecated -- use java.net.Authenticator instead
* @see java.net.Authenticator
*/
//
// REMIND: Unless compatibility with sun.* API's from 1.2 to 2.0 is
// a goal, there's no reason to carry this forward into JDK 2.0.
@Deprecated
public interface HttpAuthenticator {
/**
* Indicate whether the specified authentication scheme is
* supported. In accordance with HTTP specifications, the
* scheme name should be checked in a case-insensitive fashion.
*/
boolean schemeSupported (String scheme);
/**
* Returns the String that should be included in the HTTP
* <B>Authorization</B> field. Return null if no info was
* supplied or could be found.
* <P>
* Example:
* <pre>{@code
* --> GET http://www.authorization-required.com/ HTTP/1.0
* <-- HTTP/1.0 403 Unauthorized
* <-- WWW-Authenticate: Basic realm="WallyWorld"
* call schemeSupported("Basic"); (return true)
* call authString(u, "Basic", "WallyWorld", null);
* return "QWadhgWERghghWERfdfQ=="
* --> GET http://www.authorization-required.com/ HTTP/1.0
* --> Authorization: Basic QWadhgWERghghWERfdfQ==
* <-- HTTP/1.0 200 OK}
* <B> YAY!!!</B>
* </pre>
*/
public String authString (URL u, String scheme, String realm);
}

View file

@ -0,0 +1,113 @@
/*
* Copyright (c) 2009, 2016, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.http;
import java.net.Authenticator;
import java.net.Authenticator.RequestorType;
import java.net.InetAddress;
import java.net.URL;
/**
* Used in HTTP/Negotiate, to feed HTTP request info into JGSS as a HttpCaller,
* so that special actions can be taken, including special callback handler,
* special useSubjectCredsOnly value.
*
* This is an immutable class. It can be instantiated in two styles;
*
* 1. Un-schemed: Create at the beginning before the preferred scheme is
* determined. This object can be fed into AuthenticationHeader to check
* for the preference.
*
* 2. Schemed: With the scheme field filled, can be used in JGSS-API calls.
*/
public final class HttpCallerInfo {
// All info that an Authenticator needs.
public final URL url;
public final String host, protocol, prompt, scheme;
public final int port;
public final InetAddress addr;
public final RequestorType authType;
public final Authenticator authenticator;
/**
* Create a schemed object based on an un-schemed one.
*/
public HttpCallerInfo(HttpCallerInfo old, String scheme) {
this.url = old.url;
this.host = old.host;
this.protocol = old.protocol;
this.prompt = old.prompt;
this.port = old.port;
this.addr = old.addr;
this.authType = old.authType;
this.scheme = scheme;
this.authenticator = old.authenticator;
}
/**
* Constructor an un-schemed object for site access.
*/
public HttpCallerInfo(URL url, Authenticator a) {
this.url= url;
prompt = "";
host = url.getHost();
int p = url.getPort();
if (p == -1) {
port = url.getDefaultPort();
} else {
port = p;
}
InetAddress ia;
try {
ia = InetAddress.getByName(url.getHost());
} catch (Exception e) {
ia = null;
}
addr = ia;
protocol = url.getProtocol();
authType = RequestorType.SERVER;
scheme = "";
authenticator = a;
}
/**
* Constructor an un-schemed object for proxy access.
*/
public HttpCallerInfo(URL url, String host, int port, Authenticator a) {
this.url= url;
this.host = host;
this.port = port;
prompt = "";
addr = null;
protocol = url.getProtocol();
authType = RequestorType.PROXY;
scheme = "";
authenticator = a;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,153 @@
/*
* Copyright (c) 2009, 2016, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.http;
import java.net.URL;
import java.net.PasswordAuthentication;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import sun.util.logging.PlatformLogger;
/**
* Proxy class for loading NTLMAuthentication, so as to remove static
* dependancy.
*/
class NTLMAuthenticationProxy {
private static Method supportsTA;
private static Method isTrustedSite;
private static final String clazzStr = "sun.net.www.protocol.http.ntlm.NTLMAuthentication";
private static final String supportsTAStr = "supportsTransparentAuth";
private static final String isTrustedSiteStr = "isTrustedSite";
static final NTLMAuthenticationProxy proxy = tryLoadNTLMAuthentication();
static final boolean supported = proxy != null ? true : false;
static final boolean supportsTransparentAuth = supported ? supportsTransparentAuth() : false;
private final Constructor<? extends AuthenticationInfo> fourArgCtr;
private final Constructor<? extends AuthenticationInfo> sixArgCtr;
private NTLMAuthenticationProxy(Constructor<? extends AuthenticationInfo> fourArgCtr,
Constructor<? extends AuthenticationInfo> sixArgCtr) {
this.fourArgCtr = fourArgCtr;
this.sixArgCtr = sixArgCtr;
}
AuthenticationInfo create(boolean isProxy,
URL url,
PasswordAuthentication pw,
String authenticatorKey) {
try {
return fourArgCtr.newInstance(isProxy, url, pw, authenticatorKey);
} catch (ReflectiveOperationException roe) {
finest(roe);
}
return null;
}
AuthenticationInfo create(boolean isProxy,
String host,
int port,
PasswordAuthentication pw,
String authenticatorKey) {
try {
return sixArgCtr.newInstance(isProxy, host, port, pw, authenticatorKey);
} catch (ReflectiveOperationException roe) {
finest(roe);
}
return null;
}
/* Returns true if the NTLM implementation supports transparent
* authentication (try with the current users credentials before
* prompting for username and password, etc).
*/
private static boolean supportsTransparentAuth() {
try {
return (Boolean)supportsTA.invoke(null);
} catch (ReflectiveOperationException roe) {
finest(roe);
}
return false;
}
/* Transparent authentication should only be tried with a trusted
* site ( when running in a secure environment ).
*/
public static boolean isTrustedSite(URL url) {
try {
return (Boolean)isTrustedSite.invoke(null, url);
} catch (ReflectiveOperationException roe) {
finest(roe);
}
return false;
}
/**
* Loads the NTLM authentiation implementation through reflection. If
* the class is present, then it must have the required constructors and
* method. Otherwise, it is considered an error.
*/
@SuppressWarnings("unchecked")
private static NTLMAuthenticationProxy tryLoadNTLMAuthentication() {
Class<? extends AuthenticationInfo> cl;
Constructor<? extends AuthenticationInfo> fourArg, sixArg;
try {
cl = (Class<? extends AuthenticationInfo>)Class.forName(clazzStr, true, null);
if (cl != null) {
fourArg = cl.getConstructor(boolean.class,
URL.class,
PasswordAuthentication.class,
String.class);
sixArg = cl.getConstructor(boolean.class,
String.class,
int.class,
PasswordAuthentication.class,
String.class);
supportsTA = cl.getDeclaredMethod(supportsTAStr);
isTrustedSite = cl.getDeclaredMethod(isTrustedSiteStr, java.net.URL.class);
return new NTLMAuthenticationProxy(fourArg,
sixArg);
}
} catch (ClassNotFoundException cnfe) {
finest(cnfe);
} catch (ReflectiveOperationException roe) {
throw new AssertionError(roe);
}
return null;
}
static void finest(Exception e) {
PlatformLogger logger = HttpURLConnection.getHttpLogger();
if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
logger.finest("NTLMAuthenticationProxy: " + e);
}
}
}

View file

@ -0,0 +1,262 @@
/*
* Copyright (c) 2005, 2016, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.http;
import java.net.URL;
import java.io.IOException;
import java.net.Authenticator.RequestorType;
import java.util.Base64;
import java.util.HashMap;
import sun.net.www.HeaderParser;
import sun.util.logging.PlatformLogger;
import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE;
import static sun.net.www.protocol.http.AuthScheme.KERBEROS;
import sun.security.action.GetPropertyAction;
/**
* NegotiateAuthentication:
*
* @author weijun.wang@sun.com
* @since 1.6
*/
class NegotiateAuthentication extends AuthenticationInfo {
private static final long serialVersionUID = 100L;
private static final PlatformLogger logger = HttpURLConnection.getHttpLogger();
private final HttpCallerInfo hci;
// These maps are used to manage the GSS availability for diffrent
// hosts. The key for both maps is the host name.
// <code>supported</code> is set when isSupported is checked,
// if it's true, a cached Negotiator is put into <code>cache</code>.
// the cache can be used only once, so after the first use, it's cleaned.
static HashMap <String, Boolean> supported = null;
static ThreadLocal <HashMap <String, Negotiator>> cache = null;
/* Whether cache is enabled for Negotiate/Kerberos */
private static final boolean cacheSPNEGO;
static {
String spnegoCacheProp =
GetPropertyAction.privilegedGetProperty("jdk.spnego.cache", "true");
cacheSPNEGO = Boolean.parseBoolean(spnegoCacheProp);
}
// The HTTP Negotiate Helper
private Negotiator negotiator = null;
/**
* Constructor used for both WWW and proxy entries.
* @param hci a schemed object.
*/
public NegotiateAuthentication(HttpCallerInfo hci) {
super(RequestorType.PROXY==hci.authType ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
hci.scheme.equalsIgnoreCase("Negotiate") ? NEGOTIATE : KERBEROS,
hci.url,
"",
AuthenticatorKeys.getKey(hci.authenticator));
this.hci = hci;
}
/**
* @return true if this authentication supports preemptive authorization
*/
@Override
public boolean supportsPreemptiveAuthorization() {
return false;
}
/**
* Find out if the HttpCallerInfo supports Negotiate protocol.
* @return true if supported
*/
public static boolean isSupported(HttpCallerInfo hci) {
ClassLoader loader = null;
try {
loader = Thread.currentThread().getContextClassLoader();
} catch (SecurityException se) {
if (logger.isLoggable(PlatformLogger.Level.FINER)) {
logger.finer("NegotiateAuthentication: " +
"Attempt to get the context class loader failed - " + se);
}
}
if (loader != null) {
// Lock on the class loader instance to avoid the deadlock engaging
// the lock in "ClassLoader.loadClass(String, boolean)" method.
synchronized (loader) {
return isSupportedImpl(hci);
}
}
return isSupportedImpl(hci);
}
/**
* Find out if the HttpCallerInfo supports Negotiate protocol. In order to
* find out yes or no, an initialization of a Negotiator object against it
* is tried. The generated object will be cached under the name of ths
* hostname at a success try.<br>
*
* If this method is called for the second time on an HttpCallerInfo with
* the same hostname, the answer is retrieved from cache.
*
* @return true if supported
*/
private static synchronized boolean isSupportedImpl(HttpCallerInfo hci) {
if (supported == null) {
supported = new HashMap<>();
}
String hostname = hci.host;
hostname = hostname.toLowerCase();
if (supported.containsKey(hostname)) {
return supported.get(hostname);
}
Negotiator neg = Negotiator.getNegotiator(hci);
if (neg != null) {
supported.put(hostname, true);
// the only place cache.put is called. here we can make sure
// the object is valid and the oneToken inside is not null
if (cache == null) {
cache = new ThreadLocal<>() {
@Override
protected HashMap<String, Negotiator> initialValue() {
return new HashMap<>();
}
};
}
cache.get().put(hostname, neg);
return true;
} else {
supported.put(hostname, false);
return false;
}
}
private static synchronized HashMap<String, Negotiator> getCache() {
if (cache == null) return null;
return cache.get();
}
@Override
protected boolean useAuthCache() {
return super.useAuthCache() && cacheSPNEGO;
}
/**
* Not supported. Must use the setHeaders() method
*/
@Override
public String getHeaderValue(URL url, String method) {
throw new RuntimeException ("getHeaderValue not supported");
}
/**
* Check if the header indicates that the current auth. parameters are stale.
* If so, then replace the relevant field with the new value
* and return true. Otherwise return false.
* returning true means the request can be retried with the same userid/password
* returning false means we have to go back to the user to ask for a new
* username password.
*/
@Override
public boolean isAuthorizationStale (String header) {
return false; /* should not be called for Negotiate */
}
/**
* Set header(s) on the given connection.
* @param conn The connection to apply the header(s) to
* @param p A source of header values for this connection, not used because
* HeaderParser converts the fields to lower case, use raw instead
* @param raw The raw header field.
* @return true if all goes well, false if no headers were set.
*/
@Override
public synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
try {
String response;
byte[] incoming = null;
String[] parts = raw.split("\\s+");
if (parts.length > 1) {
incoming = Base64.getDecoder().decode(parts[1]);
}
response = hci.scheme + " " + Base64.getEncoder().encodeToString(
incoming==null?firstToken():nextToken(incoming));
conn.setAuthenticationProperty(getHeaderName(), response);
return true;
} catch (IOException e) {
return false;
}
}
/**
* return the first token.
* @return the token
* @throws IOException if <code>Negotiator.getNegotiator()</code> or
* <code>Negotiator.firstToken()</code> failed.
*/
private byte[] firstToken() throws IOException {
negotiator = null;
HashMap <String, Negotiator> cachedMap = getCache();
if (cachedMap != null) {
negotiator = cachedMap.get(getHost());
if (negotiator != null) {
cachedMap.remove(getHost()); // so that it is only used once
}
}
if (negotiator == null) {
negotiator = Negotiator.getNegotiator(hci);
if (negotiator == null) {
IOException ioe = new IOException("Cannot initialize Negotiator");
throw ioe;
}
}
return negotiator.firstToken();
}
/**
* return more tokens
* @param token the token to be fed into <code>negotiator.nextToken()</code>
* @return the token
* @throws IOException if <code>negotiator.nextToken()</code> throws Exception.
* May happen if the input token is invalid.
*/
private byte[] nextToken(byte[] token) throws IOException {
return negotiator.nextToken(token);
}
// MS will send a final WWW-Authenticate even if the status is already
// 200 OK. The token can be fed into initSecContext() again to determine
// if the server can be trusted. This is not the same concept as Digest's
// Authentication-Info header.
//
// Currently we ignore this header.
}

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2009, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.http;
import java.io.IOException;
import java.lang.reflect.Constructor;
import sun.util.logging.PlatformLogger;
/**
* This abstract class is a bridge to connect NegotiteAuthentication and
* NegotiatorImpl, so that JAAS and JGSS calls can be made
*/
public abstract class Negotiator {
static Negotiator getNegotiator(HttpCallerInfo hci) {
// These lines are equivalent to
// return new NegotiatorImpl(hci);
// The current implementation will make sure NegotiatorImpl is not
// directly referenced when compiling, thus smooth the way of building
// the J2SE platform where HttpURLConnection is a bootstrap class.
//
// Makes NegotiatorImpl, and the security classes it references, a
// runtime dependency rather than a static one.
Class<?> clazz;
Constructor<?> c;
try {
clazz = Class.forName("sun.net.www.protocol.http.spnego.NegotiatorImpl",
true,
ClassLoader.getPlatformClassLoader());
c = clazz.getConstructor(HttpCallerInfo.class);
} catch (ClassNotFoundException cnfe) {
finest(cnfe);
return null;
} catch (ReflectiveOperationException roe) {
// if the class is there then something seriously wrong if
// the constructor is not.
throw new AssertionError(roe);
}
try {
return (Negotiator) (c.newInstance(hci));
} catch (ReflectiveOperationException roe) {
finest(roe);
Throwable t = roe.getCause();
if (t != null && t instanceof Exception)
finest((Exception)t);
return null;
}
}
public abstract byte[] firstToken() throws IOException;
public abstract byte[] nextToken(byte[] in) throws IOException;
private static void finest(Exception e) {
PlatformLogger logger = HttpURLConnection.getHttpLogger();
if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
logger.finest("NegotiateAuthentication: " + e);
}
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2011, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.http.ntlm;
import java.net.URL;
/**
* This class is used to call back to deployment to determine if a given
* URL is trusted. Transparent authentication (try with logged in users
* credentials without prompting) should only be tried with trusted sites.
*/
public abstract class NTLMAuthenticationCallback {
private static volatile NTLMAuthenticationCallback callback =
new DefaultNTLMAuthenticationCallback();
public static void setNTLMAuthenticationCallback(
NTLMAuthenticationCallback callback) {
NTLMAuthenticationCallback.callback = callback;
}
public static NTLMAuthenticationCallback getNTLMAuthenticationCallback() {
return callback;
}
/**
* Returns true if the given site is trusted, i.e. we can try
* transparent Authentication.
*/
public abstract boolean isTrustedSite(URL url);
static class DefaultNTLMAuthenticationCallback extends NTLMAuthenticationCallback {
@Override
public boolean isTrustedSite(URL url) { return true; }
}
}

View file

@ -0,0 +1,299 @@
/*
* Copyright (c) 2001, 2015, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.https;
import java.net.URL;
import java.net.Proxy;
import java.net.SecureCacheResponse;
import java.security.Principal;
import java.io.IOException;
import java.util.List;
import javax.net.ssl.SSLPeerUnverifiedException;
import sun.net.www.http.*;
import sun.net.www.protocol.http.HttpURLConnection;
/**
* HTTPS URL connection support.
* We need this delegate because HttpsURLConnection is a subclass of
* java.net.HttpURLConnection. We will avoid copying over the code from
* sun.net.www.protocol.http.HttpURLConnection by having this class
*
*/
public abstract class AbstractDelegateHttpsURLConnection extends
HttpURLConnection {
protected AbstractDelegateHttpsURLConnection(URL url,
sun.net.www.protocol.http.Handler handler) throws IOException {
this(url, null, handler);
}
protected AbstractDelegateHttpsURLConnection(URL url, Proxy p,
sun.net.www.protocol.http.Handler handler) throws IOException {
super(url, p, handler);
}
protected abstract javax.net.ssl.SSLSocketFactory getSSLSocketFactory();
protected abstract javax.net.ssl.HostnameVerifier getHostnameVerifier();
/**
* No user application is able to call these routines, as no one
* should ever get access to an instance of
* DelegateHttpsURLConnection (sun.* or com.*)
*/
/**
* Create a new HttpClient object, bypassing the cache of
* HTTP client objects/connections.
*
* Note: this method is changed from protected to public because
* the com.sun.ssl.internal.www.protocol.https handler reuses this
* class for its actual implemantation
*
* @param url the URL being accessed
*/
public void setNewClient (URL url)
throws IOException {
setNewClient (url, false);
}
/**
* Obtain a HttpClient object. Use the cached copy if specified.
*
* Note: this method is changed from protected to public because
* the com.sun.ssl.internal.www.protocol.https handler reuses this
* class for its actual implemantation
*
* @param url the URL being accessed
* @param useCache whether the cached connection should be used
* if present
*/
public void setNewClient (URL url, boolean useCache)
throws IOException {
http = HttpsClient.New (getSSLSocketFactory(),
url,
getHostnameVerifier(),
useCache, this);
((HttpsClient)http).afterConnect();
}
/**
* Create a new HttpClient object, set up so that it uses
* per-instance proxying to the given HTTP proxy. This
* bypasses the cache of HTTP client objects/connections.
*
* Note: this method is changed from protected to public because
* the com.sun.ssl.internal.www.protocol.https handler reuses this
* class for its actual implemantation
*
* @param url the URL being accessed
* @param proxyHost the proxy host to use
* @param proxyPort the proxy port to use
*/
public void setProxiedClient (URL url, String proxyHost, int proxyPort)
throws IOException {
setProxiedClient(url, proxyHost, proxyPort, false);
}
/**
* Obtain a HttpClient object, set up so that it uses per-instance
* proxying to the given HTTP proxy. Use the cached copy of HTTP
* client objects/connections if specified.
*
* Note: this method is changed from protected to public because
* the com.sun.ssl.internal.www.protocol.https handler reuses this
* class for its actual implemantation
*
* @param url the URL being accessed
* @param proxyHost the proxy host to use
* @param proxyPort the proxy port to use
* @param useCache whether the cached connection should be used
* if present
*/
public void setProxiedClient (URL url, String proxyHost, int proxyPort,
boolean useCache) throws IOException {
proxiedConnect(url, proxyHost, proxyPort, useCache);
if (!http.isCachedConnection()) {
doTunneling();
}
((HttpsClient)http).afterConnect();
}
protected void proxiedConnect(URL url, String proxyHost, int proxyPort,
boolean useCache) throws IOException {
if (connected)
return;
http = HttpsClient.New (getSSLSocketFactory(),
url,
getHostnameVerifier(),
proxyHost, proxyPort, useCache, this);
connected = true;
}
/**
* Used by subclass to access "connected" variable.
*/
public boolean isConnected() {
return connected;
}
/**
* Used by subclass to access "connected" variable.
*/
public void setConnected(boolean conn) {
connected = conn;
}
/**
* Implements the HTTP protocol handler's "connect" method,
* establishing an SSL connection to the server as necessary.
*/
public void connect() throws IOException {
if (connected)
return;
plainConnect();
if (cachedResponse != null) {
// using cached response
return;
}
if (!http.isCachedConnection() && http.needsTunneling()) {
doTunneling();
}
((HttpsClient)http).afterConnect();
}
// will try to use cached HttpsClient
protected HttpClient getNewHttpClient(URL url, Proxy p, int connectTimeout)
throws IOException {
return HttpsClient.New(getSSLSocketFactory(), url,
getHostnameVerifier(), p, true, connectTimeout,
this);
}
// will open new connection
protected HttpClient getNewHttpClient(URL url, Proxy p, int connectTimeout,
boolean useCache)
throws IOException {
return HttpsClient.New(getSSLSocketFactory(), url,
getHostnameVerifier(), p,
useCache, connectTimeout, this);
}
/**
* Returns the cipher suite in use on this connection.
*/
public String getCipherSuite () {
if (cachedResponse != null) {
return ((SecureCacheResponse)cachedResponse).getCipherSuite();
}
if (http == null) {
throw new IllegalStateException("connection not yet open");
} else {
return ((HttpsClient)http).getCipherSuite ();
}
}
/**
* Returns the certificate chain the client sent to the
* server, or null if the client did not authenticate.
*/
public java.security.cert.Certificate[] getLocalCertificates() {
if (cachedResponse != null) {
List<java.security.cert.Certificate> l = ((SecureCacheResponse)cachedResponse).getLocalCertificateChain();
if (l == null) {
return null;
} else {
return l.toArray(new java.security.cert.Certificate[0]);
}
}
if (http == null) {
throw new IllegalStateException("connection not yet open");
} else {
return (((HttpsClient)http).getLocalCertificates ());
}
}
/**
* Returns the server's certificate chain, or throws
* SSLPeerUnverified Exception if
* the server did not authenticate.
*/
public java.security.cert.Certificate[] getServerCertificates()
throws SSLPeerUnverifiedException {
if (cachedResponse != null) {
List<java.security.cert.Certificate> l =
((SecureCacheResponse)cachedResponse)
.getServerCertificateChain();
if (l == null) {
return null;
} else {
return l.toArray(new java.security.cert.Certificate[0]);
}
}
if (http == null) {
throw new IllegalStateException("connection not yet open");
} else {
return (((HttpsClient)http).getServerCertificates ());
}
}
/**
* Returns the server's principal, or throws SSLPeerUnverifiedException
* if the server did not authenticate.
*/
Principal getPeerPrincipal()
throws SSLPeerUnverifiedException
{
if (cachedResponse != null) {
return ((SecureCacheResponse)cachedResponse).getPeerPrincipal();
}
if (http == null) {
throw new IllegalStateException("connection not yet open");
} else {
return (((HttpsClient)http).getPeerPrincipal());
}
}
/**
* Returns the principal the client sent to the
* server, or null if the client did not authenticate.
*/
Principal getLocalPrincipal()
{
if (cachedResponse != null) {
return ((SecureCacheResponse)cachedResponse).getLocalPrincipal();
}
if (http == null) {
throw new IllegalStateException("connection not yet open");
} else {
return (((HttpsClient)http).getLocalPrincipal());
}
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2001, 2017, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.https;
import java.net.URL;
import java.net.Proxy;
import java.io.IOException;
/**
* This class was introduced to provide an additional level of
* abstraction between javax.net.ssl.HttpURLConnection and
* com.sun.net.ssl.HttpURLConnection objects. <p>
*
* javax.net.ssl.HttpURLConnection is used in the new sun.net version
* of protocol implementation (this one)
* com.sun.net.ssl.HttpURLConnection is used in the com.sun version.
*
*/
public class DelegateHttpsURLConnection extends AbstractDelegateHttpsURLConnection {
// we need a reference to the HttpsURLConnection to get
// the properties set there
// we also need it to be public so that it can be referenced
// from sun.net.www.protocol.http.HttpURLConnection
// this is for ResponseCache.put(URI, URLConnection)
// second parameter needs to be cast to javax.net.ssl.HttpsURLConnection
// instead of AbstractDelegateHttpsURLConnection
public javax.net.ssl.HttpsURLConnection httpsURLConnection;
DelegateHttpsURLConnection(URL url,
sun.net.www.protocol.http.Handler handler,
javax.net.ssl.HttpsURLConnection httpsURLConnection)
throws IOException {
this(url, null, handler, httpsURLConnection);
}
DelegateHttpsURLConnection(URL url, Proxy p,
sun.net.www.protocol.http.Handler handler,
javax.net.ssl.HttpsURLConnection httpsURLConnection)
throws IOException {
super(url, p, handler);
this.httpsURLConnection = httpsURLConnection;
}
protected javax.net.ssl.SSLSocketFactory getSSLSocketFactory() {
return httpsURLConnection.getSSLSocketFactory();
}
protected javax.net.ssl.HostnameVerifier getHostnameVerifier() {
return httpsURLConnection.getHostnameVerifier();
}
/*
* Called by layered delegator's finalize() method to handle closing
* the underlying object.
*/
@SuppressWarnings("deprecation")
protected void dispose() throws Throwable {
super.finalize();
}
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2001, 2003, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*-
* HTTP stream opener
*/
package sun.net.www.protocol.https;
import java.io.IOException;
import java.net.URL;
import java.net.Proxy;
/** open an http input stream given a URL */
public class Handler extends sun.net.www.protocol.http.Handler {
protected String proxy;
protected int proxyPort;
protected int getDefaultPort() {
return 443;
}
public Handler () {
proxy = null;
proxyPort = -1;
}
public Handler (String proxy, int port) {
this.proxy = proxy;
this.proxyPort = port;
}
protected java.net.URLConnection openConnection(URL u)
throws IOException {
return openConnection(u, (Proxy)null);
}
protected java.net.URLConnection openConnection(URL u, Proxy p)
throws IOException {
return new HttpsURLConnectionImpl(u, p, this);
}
}

View file

@ -0,0 +1,786 @@
/*
* Copyright (c) 2001, 2016, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.https;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.PrintStream;
import java.io.BufferedOutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.URL;
import java.net.UnknownHostException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.security.Principal;
import java.security.cert.*;
import java.util.Objects;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.security.auth.x500.X500Principal;
import javax.net.ssl.*;
import sun.net.www.http.HttpClient;
import sun.net.www.protocol.http.AuthenticatorKeys;
import sun.net.www.protocol.http.HttpURLConnection;
import sun.security.action.*;
import sun.security.util.HostnameChecker;
import sun.security.ssl.SSLSocketImpl;
import sun.util.logging.PlatformLogger;
import static sun.net.www.protocol.http.HttpURLConnection.TunnelState.*;
/**
* This class provides HTTPS client URL support, building on the standard
* "sun.net.www" HTTP protocol handler. HTTPS is the same protocol as HTTP,
* but differs in the transport layer which it uses: <UL>
*
* <LI>There's a <em>Secure Sockets Layer</em> between TCP
* and the HTTP protocol code.
*
* <LI>It uses a different default TCP port.
*
* <LI>It doesn't use application level proxies, which can see and
* manipulate HTTP user level data, compromising privacy. It uses
* low level tunneling instead, which hides HTTP protocol and data
* from all third parties. (Traffic analysis is still possible).
*
* <LI>It does basic server authentication, to protect
* against "URL spoofing" attacks. This involves deciding
* whether the X.509 certificate chain identifying the server
* is trusted, and verifying that the name of the server is
* found in the certificate. (The application may enable an
* anonymous SSL cipher suite, and such checks are not done
* for anonymous ciphers.)
*
* <LI>It exposes key SSL session attributes, specifically the
* cipher suite in use and the server's X509 certificates, to
* application software which knows about this protocol handler.
*
* </UL>
*
* <P> System properties used include: <UL>
*
* <LI><em>https.proxyHost</em> ... the host supporting SSL
* tunneling using the conventional CONNECT syntax
*
* <LI><em>https.proxyPort</em> ... port to use on proxyHost
*
* <LI><em>https.cipherSuites</em> ... comma separated list of
* SSL cipher suite names to enable.
*
* <LI><em>http.nonProxyHosts</em> ...
*
* </UL>
*
* @author David Brownell
* @author Bill Foote
*/
// final for export control reasons (access to APIs); remove with care
final class HttpsClient extends HttpClient
implements HandshakeCompletedListener
{
// STATIC STATE and ACCESSORS THERETO
// HTTPS uses a different default port number than HTTP.
private static final int httpsPortNumber = 443;
// default HostnameVerifier class canonical name
private static final String defaultHVCanonicalName =
"javax.net.ssl.HttpsURLConnection.DefaultHostnameVerifier";
/** Returns the default HTTPS port (443) */
@Override
protected int getDefaultPort() { return httpsPortNumber; }
private HostnameVerifier hv;
private SSLSocketFactory sslSocketFactory;
// HttpClient.proxyDisabled will always be false, because we don't
// use an application-level HTTP proxy. We might tunnel through
// our http proxy, though.
// INSTANCE DATA
// last negotiated SSL session
private SSLSession session;
private String [] getCipherSuites() {
//
// If ciphers are assigned, sort them into an array.
//
String ciphers [];
String cipherString =
GetPropertyAction.privilegedGetProperty("https.cipherSuites");
if (cipherString == null || "".equals(cipherString)) {
ciphers = null;
} else {
StringTokenizer tokenizer;
Vector<String> v = new Vector<String>();
tokenizer = new StringTokenizer(cipherString, ",");
while (tokenizer.hasMoreTokens())
v.addElement(tokenizer.nextToken());
ciphers = new String [v.size()];
for (int i = 0; i < ciphers.length; i++)
ciphers [i] = v.elementAt(i);
}
return ciphers;
}
private String [] getProtocols() {
//
// If protocols are assigned, sort them into an array.
//
String protocols [];
String protocolString =
GetPropertyAction.privilegedGetProperty("https.protocols");
if (protocolString == null || "".equals(protocolString)) {
protocols = null;
} else {
StringTokenizer tokenizer;
Vector<String> v = new Vector<String>();
tokenizer = new StringTokenizer(protocolString, ",");
while (tokenizer.hasMoreTokens())
v.addElement(tokenizer.nextToken());
protocols = new String [v.size()];
for (int i = 0; i < protocols.length; i++) {
protocols [i] = v.elementAt(i);
}
}
return protocols;
}
private String getUserAgent() {
String userAgent =
GetPropertyAction.privilegedGetProperty("https.agent");
if (userAgent == null || userAgent.length() == 0) {
userAgent = "JSSE";
}
return userAgent;
}
// CONSTRUCTOR, FACTORY
/**
* Create an HTTPS client URL. Traffic will be tunneled through any
* intermediate nodes rather than proxied, so that confidentiality
* of data exchanged can be preserved. However, note that all the
* anonymous SSL flavors are subject to "person-in-the-middle"
* attacks against confidentiality. If you enable use of those
* flavors, you may be giving up the protection you get through
* SSL tunneling.
*
* Use New to get new HttpsClient. This constructor is meant to be
* used only by New method. New properly checks for URL spoofing.
*
* @param URL https URL with which a connection must be established
*/
private HttpsClient(SSLSocketFactory sf, URL url)
throws IOException
{
// HttpClient-level proxying is always disabled,
// because we override doConnect to do tunneling instead.
this(sf, url, (String)null, -1);
}
/**
* Create an HTTPS client URL. Traffic will be tunneled through
* the specified proxy server.
*/
HttpsClient(SSLSocketFactory sf, URL url, String proxyHost, int proxyPort)
throws IOException {
this(sf, url, proxyHost, proxyPort, -1);
}
/**
* Create an HTTPS client URL. Traffic will be tunneled through
* the specified proxy server, with a connect timeout
*/
HttpsClient(SSLSocketFactory sf, URL url, String proxyHost, int proxyPort,
int connectTimeout)
throws IOException {
this(sf, url,
(proxyHost == null? null:
HttpClient.newHttpProxy(proxyHost, proxyPort, "https")),
connectTimeout);
}
/**
* Same as previous constructor except using a Proxy
*/
HttpsClient(SSLSocketFactory sf, URL url, Proxy proxy,
int connectTimeout)
throws IOException {
PlatformLogger logger = HttpURLConnection.getHttpLogger();
if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
logger.finest("Creating new HttpsClient with url:" + url + " and proxy:" + proxy +
" with connect timeout:" + connectTimeout);
}
this.proxy = proxy;
setSSLSocketFactory(sf);
this.proxyDisabled = true;
this.host = url.getHost();
this.url = url;
port = url.getPort();
if (port == -1) {
port = getDefaultPort();
}
setConnectTimeout(connectTimeout);
openServer();
}
// This code largely ripped off from HttpClient.New, and
// it uses the same keepalive cache.
static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
HttpURLConnection httpuc)
throws IOException {
return HttpsClient.New(sf, url, hv, true, httpuc);
}
/** See HttpClient for the model for this method. */
static HttpClient New(SSLSocketFactory sf, URL url,
HostnameVerifier hv, boolean useCache,
HttpURLConnection httpuc) throws IOException {
return HttpsClient.New(sf, url, hv, (String)null, -1, useCache, httpuc);
}
/**
* Get a HTTPS client to the URL. Traffic will be tunneled through
* the specified proxy server.
*/
static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
String proxyHost, int proxyPort,
HttpURLConnection httpuc) throws IOException {
return HttpsClient.New(sf, url, hv, proxyHost, proxyPort, true, httpuc);
}
static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
String proxyHost, int proxyPort, boolean useCache,
HttpURLConnection httpuc)
throws IOException {
return HttpsClient.New(sf, url, hv, proxyHost, proxyPort, useCache, -1,
httpuc);
}
static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
String proxyHost, int proxyPort, boolean useCache,
int connectTimeout, HttpURLConnection httpuc)
throws IOException {
return HttpsClient.New(sf, url, hv,
(proxyHost == null? null :
HttpClient.newHttpProxy(proxyHost, proxyPort, "https")),
useCache, connectTimeout, httpuc);
}
static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
Proxy p, boolean useCache,
int connectTimeout, HttpURLConnection httpuc)
throws IOException
{
if (p == null) {
p = Proxy.NO_PROXY;
}
PlatformLogger logger = HttpURLConnection.getHttpLogger();
if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
logger.finest("Looking for HttpClient for URL " + url +
" and proxy value of " + p);
}
HttpsClient ret = null;
if (useCache) {
/* see if one's already around */
ret = (HttpsClient) kac.get(url, sf);
if (ret != null && httpuc != null &&
httpuc.streaming() &&
httpuc.getRequestMethod() == "POST") {
if (!ret.available())
ret = null;
}
if (ret != null) {
String ak = httpuc == null ? AuthenticatorKeys.DEFAULT
: httpuc.getAuthenticatorKey();
boolean compatible = ((ret.proxy != null && ret.proxy.equals(p)) ||
(ret.proxy == null && p == Proxy.NO_PROXY))
&& Objects.equals(ret.getAuthenticatorKey(), ak);
if (compatible) {
synchronized (ret) {
ret.cachedHttpClient = true;
assert ret.inCache;
ret.inCache = false;
if (httpuc != null && ret.needsTunneling())
httpuc.setTunnelState(TUNNELING);
if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
logger.finest("KeepAlive stream retrieved from the cache, " + ret);
}
}
} else {
// We cannot return this connection to the cache as it's
// KeepAliveTimeout will get reset. We simply close the connection.
// This should be fine as it is very rare that a connection
// to the same host will not use the same proxy.
synchronized(ret) {
if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
logger.finest("Not returning this connection to cache: " + ret);
}
ret.inCache = false;
ret.closeServer();
}
ret = null;
}
}
}
if (ret == null) {
ret = new HttpsClient(sf, url, p, connectTimeout);
if (httpuc != null) {
ret.authenticatorKey = httpuc.getAuthenticatorKey();
}
} else {
SecurityManager security = System.getSecurityManager();
if (security != null) {
if (ret.proxy == Proxy.NO_PROXY || ret.proxy == null) {
security.checkConnect(InetAddress.getByName(url.getHost()).getHostAddress(), url.getPort());
} else {
security.checkConnect(url.getHost(), url.getPort());
}
}
ret.url = url;
}
ret.setHostnameVerifier(hv);
return ret;
}
// METHODS
void setHostnameVerifier(HostnameVerifier hv) {
this.hv = hv;
}
void setSSLSocketFactory(SSLSocketFactory sf) {
sslSocketFactory = sf;
}
SSLSocketFactory getSSLSocketFactory() {
return sslSocketFactory;
}
/**
* The following method, createSocket, is defined in NetworkClient
* and overridden here so that the socket facroty is used to create
* new sockets.
*/
@Override
protected Socket createSocket() throws IOException {
try {
return sslSocketFactory.createSocket();
} catch (SocketException se) {
//
// bug 6771432
// javax.net.SocketFactory throws a SocketException with an
// UnsupportedOperationException as its cause to indicate that
// unconnected sockets have not been implemented.
//
Throwable t = se.getCause();
if (t != null && t instanceof UnsupportedOperationException) {
return super.createSocket();
} else {
throw se;
}
}
}
@Override
public boolean needsTunneling() {
return (proxy != null && proxy.type() != Proxy.Type.DIRECT
&& proxy.type() != Proxy.Type.SOCKS);
}
@Override
public void afterConnect() throws IOException, UnknownHostException {
if (!isCachedConnection()) {
SSLSocket s = null;
SSLSocketFactory factory = sslSocketFactory;
try {
if (!(serverSocket instanceof SSLSocket)) {
s = (SSLSocket)factory.createSocket(serverSocket,
host, port, true);
} else {
s = (SSLSocket)serverSocket;
if (s instanceof SSLSocketImpl) {
((SSLSocketImpl)s).setHost(host);
}
}
} catch (IOException ex) {
// If we fail to connect through the tunnel, try it
// locally, as a last resort. If this doesn't work,
// throw the original exception.
try {
s = (SSLSocket)factory.createSocket(host, port);
} catch (IOException ignored) {
throw ex;
}
}
//
// Force handshaking, so that we get any authentication.
// Register a handshake callback so our session state tracks any
// later session renegotiations.
//
String [] protocols = getProtocols();
String [] ciphers = getCipherSuites();
if (protocols != null) {
s.setEnabledProtocols(protocols);
}
if (ciphers != null) {
s.setEnabledCipherSuites(ciphers);
}
s.addHandshakeCompletedListener(this);
// We have two hostname verification approaches. One is in
// SSL/TLS socket layer, where the algorithm is configured with
// SSLParameters.setEndpointIdentificationAlgorithm(), and the
// hostname verification is done by X509ExtendedTrustManager when
// the algorithm is "HTTPS". The other one is in HTTPS layer,
// where the algorithm is customized by
// HttpsURLConnection.setHostnameVerifier(), and the hostname
// verification is done by HostnameVerifier when the default
// rules for hostname verification fail.
//
// The relationship between two hostname verification approaches
// likes the following:
//
// | EIA algorithm
// +----------------------------------------------
// | null | HTTPS | LDAP/other |
// -------------------------------------------------------------
// | |1 |2 |3 |
// HNV | default | Set HTTPS EIA | use EIA | HTTPS |
// |--------------------------------------------------------
// | non - |4 |5 |6 |
// | default | HTTPS/HNV | use EIA | HTTPS/HNV |
// -------------------------------------------------------------
//
// Abbreviation:
// EIA: the endpoint identification algorithm in SSL/TLS
// socket layer
// HNV: the hostname verification object in HTTPS layer
// Notes:
// case 1. default HNV and EIA is null
// Set EIA as HTTPS, hostname check done in SSL/TLS
// layer.
// case 2. default HNV and EIA is HTTPS
// Use existing EIA, hostname check done in SSL/TLS
// layer.
// case 3. default HNV and EIA is other than HTTPS
// Use existing EIA, EIA check done in SSL/TLS
// layer, then do HTTPS check in HTTPS layer.
// case 4. non-default HNV and EIA is null
// No EIA, no EIA check done in SSL/TLS layer, then do
// HTTPS check in HTTPS layer using HNV as override.
// case 5. non-default HNV and EIA is HTTPS
// Use existing EIA, hostname check done in SSL/TLS
// layer. No HNV override possible. We will review this
// decision and may update the architecture for JDK 7.
// case 6. non-default HNV and EIA is other than HTTPS
// Use existing EIA, EIA check done in SSL/TLS layer,
// then do HTTPS check in HTTPS layer as override.
boolean needToCheckSpoofing = true;
String identification =
s.getSSLParameters().getEndpointIdentificationAlgorithm();
if (identification != null && identification.length() != 0) {
if (identification.equalsIgnoreCase("HTTPS")) {
// Do not check server identity again out of SSLSocket,
// the endpoint will be identified during TLS handshaking
// in SSLSocket.
needToCheckSpoofing = false;
} // else, we don't understand the identification algorithm,
// need to check URL spoofing here.
} else {
boolean isDefaultHostnameVerifier = false;
// We prefer to let the SSLSocket do the spoof checks, but if
// the application has specified a HostnameVerifier (HNV),
// we will always use that.
if (hv != null) {
String canonicalName = hv.getClass().getCanonicalName();
if (canonicalName != null &&
canonicalName.equalsIgnoreCase(defaultHVCanonicalName)) {
isDefaultHostnameVerifier = true;
}
} else {
// Unlikely to happen! As the behavior is the same as the
// default hostname verifier, so we prefer to let the
// SSLSocket do the spoof checks.
isDefaultHostnameVerifier = true;
}
if (isDefaultHostnameVerifier) {
// If the HNV is the default from HttpsURLConnection, we
// will do the spoof checks in SSLSocket.
SSLParameters paramaters = s.getSSLParameters();
paramaters.setEndpointIdentificationAlgorithm("HTTPS");
s.setSSLParameters(paramaters);
needToCheckSpoofing = false;
}
}
s.startHandshake();
session = s.getSession();
// change the serverSocket and serverOutput
serverSocket = s;
try {
serverOutput = new PrintStream(
new BufferedOutputStream(serverSocket.getOutputStream()),
false, encoding);
} catch (UnsupportedEncodingException e) {
throw new InternalError(encoding+" encoding not found");
}
// check URL spoofing if it has not been checked under handshaking
if (needToCheckSpoofing) {
checkURLSpoofing(hv);
}
} else {
// if we are reusing a cached https session,
// we don't need to do handshaking etc. But we do need to
// set the ssl session
session = ((SSLSocket)serverSocket).getSession();
}
}
// Server identity checking is done according to RFC 2818: HTTP over TLS
// Section 3.1 Server Identity
private void checkURLSpoofing(HostnameVerifier hostnameVerifier)
throws IOException {
//
// Get authenticated server name, if any
//
String host = url.getHost();
// if IPv6 strip off the "[]"
if (host != null && host.startsWith("[") && host.endsWith("]")) {
host = host.substring(1, host.length()-1);
}
Certificate[] peerCerts = null;
String cipher = session.getCipherSuite();
try {
HostnameChecker checker = HostnameChecker.getInstance(
HostnameChecker.TYPE_TLS);
// Use ciphersuite to determine whether Kerberos is present.
if (cipher.startsWith("TLS_KRB5")) {
if (!HostnameChecker.match(host, getPeerPrincipal())) {
throw new SSLPeerUnverifiedException("Hostname checker" +
" failed for Kerberos");
}
} else { // X.509
// get the subject's certificate
peerCerts = session.getPeerCertificates();
X509Certificate peerCert;
if (peerCerts[0] instanceof
java.security.cert.X509Certificate) {
peerCert = (java.security.cert.X509Certificate)peerCerts[0];
} else {
throw new SSLPeerUnverifiedException("");
}
checker.match(host, peerCert);
}
// if it doesn't throw an exception, we passed. Return.
return;
} catch (SSLPeerUnverifiedException e) {
//
// client explicitly changed default policy and enabled
// anonymous ciphers; we can't check the standard policy
//
// ignore
} catch (java.security.cert.CertificateException cpe) {
// ignore
}
if ((cipher != null) && (cipher.indexOf("_anon_") != -1)) {
return;
} else if ((hostnameVerifier != null) &&
(hostnameVerifier.verify(host, session))) {
return;
}
serverSocket.close();
session.invalidate();
throw new IOException("HTTPS hostname wrong: should be <"
+ url.getHost() + ">");
}
@Override
protected void putInKeepAliveCache() {
if (inCache) {
assert false : "Duplicate put to keep alive cache";
return;
}
inCache = true;
kac.put(url, sslSocketFactory, this);
}
/*
* Close an idle connection to this URL (if it exists in the cache).
*/
@Override
public void closeIdleConnection() {
HttpClient http = kac.get(url, sslSocketFactory);
if (http != null) {
http.closeServer();
}
}
/**
* Returns the cipher suite in use on this connection.
*/
String getCipherSuite() {
return session.getCipherSuite();
}
/**
* Returns the certificate chain the client sent to the
* server, or null if the client did not authenticate.
*/
public java.security.cert.Certificate [] getLocalCertificates() {
return session.getLocalCertificates();
}
/**
* Returns the certificate chain with which the server
* authenticated itself, or throw a SSLPeerUnverifiedException
* if the server did not authenticate.
*/
java.security.cert.Certificate [] getServerCertificates()
throws SSLPeerUnverifiedException
{
return session.getPeerCertificates();
}
/**
* Returns the principal with which the server authenticated
* itself, or throw a SSLPeerUnverifiedException if the
* server did not authenticate.
*/
Principal getPeerPrincipal()
throws SSLPeerUnverifiedException
{
Principal principal;
try {
principal = session.getPeerPrincipal();
} catch (AbstractMethodError e) {
// if the provider does not support it, fallback to peer certs.
// return the X500Principal of the end-entity cert.
java.security.cert.Certificate[] certs =
session.getPeerCertificates();
principal = ((X509Certificate)certs[0]).getSubjectX500Principal();
}
return principal;
}
/**
* Returns the principal the client sent to the
* server, or null if the client did not authenticate.
*/
Principal getLocalPrincipal()
{
Principal principal;
try {
principal = session.getLocalPrincipal();
} catch (AbstractMethodError e) {
principal = null;
// if the provider does not support it, fallback to local certs.
// return the X500Principal of the end-entity cert.
java.security.cert.Certificate[] certs =
session.getLocalCertificates();
if (certs != null) {
principal = ((X509Certificate)certs[0]).getSubjectX500Principal();
}
}
return principal;
}
/**
* This method implements the SSL HandshakeCompleted callback,
* remembering the resulting session so that it may be queried
* for the current cipher suite and peer certificates. Servers
* sometimes re-initiate handshaking, so the session in use on
* a given connection may change. When sessions change, so may
* peer identities and cipher suites.
*/
public void handshakeCompleted(HandshakeCompletedEvent event)
{
session = event.getSession();
}
/**
* @return the proxy host being used for this client, or null
* if we're not going through a proxy
*/
@Override
public String getProxyHostUsed() {
if (!needsTunneling()) {
return null;
} else {
return super.getProxyHostUsed();
}
}
/**
* @return the proxy port being used for this client. Meaningless
* if getProxyHostUsed() gives null.
*/
@Override
public int getProxyPortUsed() {
return (proxy == null || proxy.type() == Proxy.Type.DIRECT ||
proxy.type() == Proxy.Type.SOCKS)? -1:
((InetSocketAddress)proxy.address()).getPort();
}
}

View file

@ -0,0 +1,527 @@
/*
* Copyright (c) 2001, 2017, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* NOTE: This class lives in the package sun.net.www.protocol.https.
* There is a copy in com.sun.net.ssl.internal.www.protocol.https for JSSE
* 1.0.2 compatibility. It is 100% identical except the package and extends
* lines. Any changes should be made to be class in sun.net.* and then copied
* to com.sun.net.*.
*/
// For both copies of the file, uncomment one line and comment the other
package sun.net.www.protocol.https;
// package com.sun.net.ssl.internal.www.protocol.https;
import java.net.URL;
import java.net.Proxy;
import java.net.ProtocolException;
import java.io.*;
import java.net.Authenticator;
import javax.net.ssl.*;
import java.security.Permission;
import java.security.Principal;
import java.util.Map;
import java.util.List;
import sun.net.www.http.HttpClient;
/**
* A class to represent an HTTP connection to a remote object.
*
* Ideally, this class should subclass and inherit the http handler
* implementation, but it can't do so because that class have the
* wrong Java Type. Thus it uses the delegate (aka, the
* Adapter/Wrapper design pattern) to reuse code from the http
* handler.
*
* Since it would use a delegate to access
* sun.net.www.protocol.http.HttpURLConnection functionalities, it
* needs to implement all public methods in it's super class and all
* the way to Object.
*
*/
// For both copies of the file, uncomment one line and comment the
// other. The differences between the two copies are introduced for
// plugin, and it is marked as such.
public class HttpsURLConnectionImpl
extends javax.net.ssl.HttpsURLConnection {
// public class HttpsURLConnectionOldImpl
// extends com.sun.net.ssl.HttpsURLConnection {
// NOTE: made protected for plugin so that subclass can set it.
protected DelegateHttpsURLConnection delegate;
// For both copies of the file, uncomment one line and comment the other
HttpsURLConnectionImpl(URL u, Handler handler) throws IOException {
// HttpsURLConnectionOldImpl(URL u, Handler handler) throws IOException {
this(u, null, handler);
}
// For both copies of the file, uncomment one line and comment the other
HttpsURLConnectionImpl(URL u, Proxy p, Handler handler) throws IOException {
// HttpsURLConnectionOldImpl(URL u, Proxy p, Handler handler) throws IOException {
super(u);
delegate = new DelegateHttpsURLConnection(url, p, handler, this);
}
// NOTE: introduced for plugin
// subclass needs to overwrite this to set delegate to
// the appropriate delegatee
protected HttpsURLConnectionImpl(URL u) throws IOException {
super(u);
}
/**
* Create a new HttpClient object, bypassing the cache of
* HTTP client objects/connections.
*
* @param url the URL being accessed
*/
protected void setNewClient(URL url) throws IOException {
delegate.setNewClient(url, false);
}
/**
* Obtain a HttpClient object. Use the cached copy if specified.
*
* @param url the URL being accessed
* @param useCache whether the cached connection should be used
* if present
*/
protected void setNewClient(URL url, boolean useCache)
throws IOException {
delegate.setNewClient(url, useCache);
}
/**
* Create a new HttpClient object, set up so that it uses
* per-instance proxying to the given HTTP proxy. This
* bypasses the cache of HTTP client objects/connections.
*
* @param url the URL being accessed
* @param proxyHost the proxy host to use
* @param proxyPort the proxy port to use
*/
protected void setProxiedClient(URL url, String proxyHost, int proxyPort)
throws IOException {
delegate.setProxiedClient(url, proxyHost, proxyPort);
}
/**
* Obtain a HttpClient object, set up so that it uses per-instance
* proxying to the given HTTP proxy. Use the cached copy of HTTP
* client objects/connections if specified.
*
* @param url the URL being accessed
* @param proxyHost the proxy host to use
* @param proxyPort the proxy port to use
* @param useCache whether the cached connection should be used
* if present
*/
protected void setProxiedClient(URL url, String proxyHost, int proxyPort,
boolean useCache) throws IOException {
delegate.setProxiedClient(url, proxyHost, proxyPort, useCache);
}
/**
* Implements the HTTP protocol handler's "connect" method,
* establishing an SSL connection to the server as necessary.
*/
public void connect() throws IOException {
delegate.connect();
}
/**
* Used by subclass to access "connected" variable. Since we are
* delegating the actual implementation to "delegate", we need to
* delegate the access of "connected" as well.
*/
protected boolean isConnected() {
return delegate.isConnected();
}
/**
* Used by subclass to access "connected" variable. Since we are
* delegating the actual implementation to "delegate", we need to
* delegate the access of "connected" as well.
*/
protected void setConnected(boolean conn) {
delegate.setConnected(conn);
}
/**
* Returns the cipher suite in use on this connection.
*/
public String getCipherSuite() {
return delegate.getCipherSuite();
}
/**
* Returns the certificate chain the client sent to the
* server, or null if the client did not authenticate.
*/
public java.security.cert.Certificate []
getLocalCertificates() {
return delegate.getLocalCertificates();
}
/**
* Returns the server's certificate chain, or throws
* SSLPeerUnverified Exception if
* the server did not authenticate.
*/
public java.security.cert.Certificate []
getServerCertificates() throws SSLPeerUnverifiedException {
return delegate.getServerCertificates();
}
/**
* Returns the principal with which the server authenticated itself,
* or throw a SSLPeerUnverifiedException if the server did not authenticate.
*/
public Principal getPeerPrincipal()
throws SSLPeerUnverifiedException
{
return delegate.getPeerPrincipal();
}
/**
* Returns the principal the client sent to the
* server, or null if the client did not authenticate.
*/
public Principal getLocalPrincipal()
{
return delegate.getLocalPrincipal();
}
/*
* Allowable input/output sequences:
* [interpreted as POST/PUT]
* - get output, [write output,] get input, [read input]
* - get output, [write output]
* [interpreted as GET]
* - get input, [read input]
* Disallowed:
* - get input, [read input,] get output, [write output]
*/
public synchronized OutputStream getOutputStream() throws IOException {
return delegate.getOutputStream();
}
public synchronized InputStream getInputStream() throws IOException {
return delegate.getInputStream();
}
public InputStream getErrorStream() {
return delegate.getErrorStream();
}
/**
* Disconnect from the server.
*/
public void disconnect() {
delegate.disconnect();
}
public boolean usingProxy() {
return delegate.usingProxy();
}
/**
* Returns an unmodifiable Map of the header fields.
* The Map keys are Strings that represent the
* response-header field names. Each Map value is an
* unmodifiable List of Strings that represents
* the corresponding field values.
*
* @return a Map of header fields
* @since 1.4
*/
public Map<String,List<String>> getHeaderFields() {
return delegate.getHeaderFields();
}
/**
* Gets a header field by name. Returns null if not known.
* @param name the name of the header field
*/
public String getHeaderField(String name) {
return delegate.getHeaderField(name);
}
/**
* Gets a header field by index. Returns null if not known.
* @param n the index of the header field
*/
public String getHeaderField(int n) {
return delegate.getHeaderField(n);
}
/**
* Gets a header field by index. Returns null if not known.
* @param n the index of the header field
*/
public String getHeaderFieldKey(int n) {
return delegate.getHeaderFieldKey(n);
}
/**
* Sets request property. If a property with the key already
* exists, overwrite its value with the new value.
* @param value the value to be set
*/
public void setRequestProperty(String key, String value) {
delegate.setRequestProperty(key, value);
}
/**
* Adds a general request property specified by a
* key-value pair. This method will not overwrite
* existing values associated with the same key.
*
* @param key the keyword by which the request is known
* (e.g., "<code>accept</code>").
* @param value the value associated with it.
* @see #getRequestProperties(java.lang.String)
* @since 1.4
*/
public void addRequestProperty(String key, String value) {
delegate.addRequestProperty(key, value);
}
/**
* Overwrite super class method
*/
public int getResponseCode() throws IOException {
return delegate.getResponseCode();
}
public String getRequestProperty(String key) {
return delegate.getRequestProperty(key);
}
/**
* Returns an unmodifiable Map of general request
* properties for this connection. The Map keys
* are Strings that represent the request-header
* field names. Each Map value is a unmodifiable List
* of Strings that represents the corresponding
* field values.
*
* @return a Map of the general request properties for this connection.
* @throws IllegalStateException if already connected
* @since 1.4
*/
public Map<String,List<String>> getRequestProperties() {
return delegate.getRequestProperties();
}
/*
* We support JDK 1.2.x so we can't count on these from JDK 1.3.
* We override and supply our own version.
*/
public void setInstanceFollowRedirects(boolean shouldFollow) {
delegate.setInstanceFollowRedirects(shouldFollow);
}
public boolean getInstanceFollowRedirects() {
return delegate.getInstanceFollowRedirects();
}
public void setRequestMethod(String method) throws ProtocolException {
delegate.setRequestMethod(method);
}
public String getRequestMethod() {
return delegate.getRequestMethod();
}
public String getResponseMessage() throws IOException {
return delegate.getResponseMessage();
}
public long getHeaderFieldDate(String name, long Default) {
return delegate.getHeaderFieldDate(name, Default);
}
public Permission getPermission() throws IOException {
return delegate.getPermission();
}
public URL getURL() {
return delegate.getURL();
}
public int getContentLength() {
return delegate.getContentLength();
}
public long getContentLengthLong() {
return delegate.getContentLengthLong();
}
public String getContentType() {
return delegate.getContentType();
}
public String getContentEncoding() {
return delegate.getContentEncoding();
}
public long getExpiration() {
return delegate.getExpiration();
}
public long getDate() {
return delegate.getDate();
}
public long getLastModified() {
return delegate.getLastModified();
}
public int getHeaderFieldInt(String name, int Default) {
return delegate.getHeaderFieldInt(name, Default);
}
public long getHeaderFieldLong(String name, long Default) {
return delegate.getHeaderFieldLong(name, Default);
}
public Object getContent() throws IOException {
return delegate.getContent();
}
@SuppressWarnings("rawtypes")
public Object getContent(Class[] classes) throws IOException {
return delegate.getContent(classes);
}
public String toString() {
return delegate.toString();
}
public void setDoInput(boolean doinput) {
delegate.setDoInput(doinput);
}
public boolean getDoInput() {
return delegate.getDoInput();
}
public void setDoOutput(boolean dooutput) {
delegate.setDoOutput(dooutput);
}
public boolean getDoOutput() {
return delegate.getDoOutput();
}
public void setAllowUserInteraction(boolean allowuserinteraction) {
delegate.setAllowUserInteraction(allowuserinteraction);
}
public boolean getAllowUserInteraction() {
return delegate.getAllowUserInteraction();
}
public void setUseCaches(boolean usecaches) {
delegate.setUseCaches(usecaches);
}
public boolean getUseCaches() {
return delegate.getUseCaches();
}
public void setIfModifiedSince(long ifmodifiedsince) {
delegate.setIfModifiedSince(ifmodifiedsince);
}
public long getIfModifiedSince() {
return delegate.getIfModifiedSince();
}
public boolean getDefaultUseCaches() {
return delegate.getDefaultUseCaches();
}
public void setDefaultUseCaches(boolean defaultusecaches) {
delegate.setDefaultUseCaches(defaultusecaches);
}
/*
* finalize (dispose) the delegated object. Otherwise
* sun.net.www.protocol.http.HttpURLConnection's finalize()
* would have to be made public.
*/
@SuppressWarnings("deprecation")
protected void finalize() throws Throwable {
delegate.dispose();
}
public boolean equals(Object obj) {
return this == obj || ((obj instanceof HttpsURLConnectionImpl) &&
delegate.equals(((HttpsURLConnectionImpl)obj).delegate));
}
public int hashCode() {
return delegate.hashCode();
}
public void setConnectTimeout(int timeout) {
delegate.setConnectTimeout(timeout);
}
public int getConnectTimeout() {
return delegate.getConnectTimeout();
}
public void setReadTimeout(int timeout) {
delegate.setReadTimeout(timeout);
}
public int getReadTimeout() {
return delegate.getReadTimeout();
}
public void setFixedLengthStreamingMode (int contentLength) {
delegate.setFixedLengthStreamingMode(contentLength);
}
public void setFixedLengthStreamingMode(long contentLength) {
delegate.setFixedLengthStreamingMode(contentLength);
}
public void setChunkedStreamingMode (int chunklen) {
delegate.setChunkedStreamingMode(chunklen);
}
@Override
public void setAuthenticator(Authenticator auth) {
delegate.setAuthenticator(auth);
}
}

View file

@ -0,0 +1,209 @@
/*
* Copyright (c) 1997, 2011, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.jar;
import java.io.IOException;
import java.net.*;
import sun.net.www.ParseUtil;
/*
* Jar URL Handler
*/
public class Handler extends java.net.URLStreamHandler {
private static final String separator = "!/";
protected java.net.URLConnection openConnection(URL u)
throws IOException {
return new JarURLConnection(u, this);
}
private static int indexOfBangSlash(String spec) {
int indexOfBang = spec.length();
while((indexOfBang = spec.lastIndexOf('!', indexOfBang)) != -1) {
if ((indexOfBang != (spec.length() - 1)) &&
(spec.charAt(indexOfBang + 1) == '/')) {
return indexOfBang + 1;
} else {
indexOfBang--;
}
}
return -1;
}
/**
* Compare two jar URLs
*/
@Override
protected boolean sameFile(URL u1, URL u2) {
if (!u1.getProtocol().equals("jar") || !u2.getProtocol().equals("jar"))
return false;
String file1 = u1.getFile();
String file2 = u2.getFile();
int sep1 = file1.indexOf(separator);
int sep2 = file2.indexOf(separator);
if (sep1 == -1 || sep2 == -1) {
return super.sameFile(u1, u2);
}
String entry1 = file1.substring(sep1 + 2);
String entry2 = file2.substring(sep2 + 2);
if (!entry1.equals(entry2))
return false;
URL enclosedURL1 = null, enclosedURL2 = null;
try {
enclosedURL1 = new URL(file1.substring(0, sep1));
enclosedURL2 = new URL(file2.substring(0, sep2));
} catch (MalformedURLException unused) {
return super.sameFile(u1, u2);
}
if (!super.sameFile(enclosedURL1, enclosedURL2)) {
return false;
}
return true;
}
@Override
protected int hashCode(URL u) {
int h = 0;
String protocol = u.getProtocol();
if (protocol != null)
h += protocol.hashCode();
String file = u.getFile();
int sep = file.indexOf(separator);
if (sep == -1)
return h + file.hashCode();
URL enclosedURL = null;
String fileWithoutEntry = file.substring(0, sep);
try {
enclosedURL = new URL(fileWithoutEntry);
h += enclosedURL.hashCode();
} catch (MalformedURLException unused) {
h += fileWithoutEntry.hashCode();
}
String entry = file.substring(sep + 2);
h += entry.hashCode();
return h;
}
@Override
@SuppressWarnings("deprecation")
protected void parseURL(URL url, String spec,
int start, int limit) {
String file = null;
String ref = null;
// first figure out if there is an anchor
int refPos = spec.indexOf('#', limit);
boolean refOnly = refPos == start;
if (refPos > -1) {
ref = spec.substring(refPos + 1, spec.length());
if (refOnly) {
file = url.getFile();
}
}
// then figure out if the spec is
// 1. absolute (jar:)
// 2. relative (i.e. url + foo/bar/baz.ext)
// 3. anchor-only (i.e. url + #foo), which we already did (refOnly)
boolean absoluteSpec = false;
if (spec.length() >= 4) {
absoluteSpec = spec.substring(0, 4).equalsIgnoreCase("jar:");
}
spec = spec.substring(start, limit);
if (absoluteSpec) {
file = parseAbsoluteSpec(spec);
} else if (!refOnly) {
file = parseContextSpec(url, spec);
// Canonize the result after the bangslash
int bangSlash = indexOfBangSlash(file);
String toBangSlash = file.substring(0, bangSlash);
String afterBangSlash = file.substring(bangSlash);
sun.net.www.ParseUtil canonizer = new ParseUtil();
afterBangSlash = canonizer.canonizeString(afterBangSlash);
file = toBangSlash + afterBangSlash;
}
setURL(url, "jar", "", -1, file, ref);
}
private String parseAbsoluteSpec(String spec) {
URL url = null;
int index = -1;
// check for !/
if ((index = indexOfBangSlash(spec)) == -1) {
throw new NullPointerException("no !/ in spec");
}
// test the inner URL
try {
String innerSpec = spec.substring(0, index - 1);
url = new URL(innerSpec);
} catch (MalformedURLException e) {
throw new NullPointerException("invalid url: " +
spec + " (" + e + ")");
}
return spec;
}
private String parseContextSpec(URL url, String spec) {
String ctxFile = url.getFile();
// if the spec begins with /, chop up the jar back !/
if (spec.startsWith("/")) {
int bangSlash = indexOfBangSlash(ctxFile);
if (bangSlash == -1) {
throw new NullPointerException("malformed " +
"context url:" +
url +
": no !/");
}
ctxFile = ctxFile.substring(0, bangSlash);
}
if (!ctxFile.endsWith("/") && (!spec.startsWith("/"))){
// chop up the last component
int lastSlash = ctxFile.lastIndexOf('/');
if (lastSlash == -1) {
throw new NullPointerException("malformed " +
"context url:" +
url);
}
ctxFile = ctxFile.substring(0, lastSlash + 1);
}
return (ctxFile + spec);
}
}

View file

@ -0,0 +1,379 @@
/*
* Copyright (c) 1997, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.jar;
import java.io.InputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.BufferedInputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.MalformedURLException;
import java.net.UnknownServiceException;
import java.util.Enumeration;
import java.util.Map;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.security.Permission;
/**
* @author Benjamin Renaud
* @since 1.2
*/
public class JarURLConnection extends java.net.JarURLConnection {
private static final boolean debug = false;
/* the Jar file factory. It handles both retrieval and caching.
*/
private static final JarFileFactory factory = JarFileFactory.getInstance();
/* the url for the Jar file */
private URL jarFileURL;
/* the permission to get this JAR file. This is the actual, ultimate,
* permission, returned by the jar file factory.
*/
private Permission permission;
/* the url connection for the JAR file */
private URLConnection jarFileURLConnection;
/* the entry name, if any */
private String entryName;
/* the JarEntry */
private JarEntry jarEntry;
/* the jar file corresponding to this connection */
private JarFile jarFile;
/* the content type for this connection */
private String contentType;
public JarURLConnection(URL url, Handler handler)
throws MalformedURLException, IOException {
super(url);
jarFileURL = getJarFileURL();
jarFileURLConnection = jarFileURL.openConnection();
// whether, or not, the embedded URL should use the cache will depend
// on this instance's cache value
jarFileURLConnection.setUseCaches(useCaches);
entryName = getEntryName();
}
public JarFile getJarFile() throws IOException {
connect();
return jarFile;
}
public JarEntry getJarEntry() throws IOException {
connect();
return jarEntry;
}
public Permission getPermission() throws IOException {
return jarFileURLConnection.getPermission();
}
class JarURLInputStream extends java.io.FilterInputStream {
JarURLInputStream (InputStream src) {
super (src);
}
public void close () throws IOException {
try {
super.close();
} finally {
if (!getUseCaches()) {
jarFile.close();
}
}
}
}
public void connect() throws IOException {
if (!connected) {
/* the factory call will do the security checks */
jarFile = factory.get(getJarFileURL(), getUseCaches());
/* we also ask the factory the permission that was required
* to get the jarFile, and set it as our permission.
*/
if (getUseCaches()) {
boolean oldUseCaches = jarFileURLConnection.getUseCaches();
jarFileURLConnection = factory.getConnection(jarFile);
jarFileURLConnection.setUseCaches(oldUseCaches);
}
if ((entryName != null)) {
jarEntry = (JarEntry)jarFile.getEntry(entryName);
if (jarEntry == null) {
try {
if (!getUseCaches()) {
jarFile.close();
}
} catch (Exception e) {
}
throw new FileNotFoundException("JAR entry " + entryName +
" not found in " +
jarFile.getName());
}
}
connected = true;
}
}
public InputStream getInputStream() throws IOException {
connect();
InputStream result = null;
if (entryName == null) {
throw new IOException("no entry name specified");
} else {
if (jarEntry == null) {
throw new FileNotFoundException("JAR entry " + entryName +
" not found in " +
jarFile.getName());
}
result = new JarURLInputStream (jarFile.getInputStream(jarEntry));
}
return result;
}
public int getContentLength() {
long result = getContentLengthLong();
if (result > Integer.MAX_VALUE)
return -1;
return (int) result;
}
public long getContentLengthLong() {
long result = -1;
try {
connect();
if (jarEntry == null) {
/* if the URL referes to an archive */
result = jarFileURLConnection.getContentLengthLong();
} else {
/* if the URL referes to an archive entry */
result = getJarEntry().getSize();
}
} catch (IOException e) {
}
return result;
}
public Object getContent() throws IOException {
Object result = null;
connect();
if (entryName == null) {
result = jarFile;
} else {
result = super.getContent();
}
return result;
}
public String getContentType() {
if (contentType == null) {
if (entryName == null) {
contentType = "x-java/jar";
} else {
try {
connect();
InputStream in = jarFile.getInputStream(jarEntry);
contentType = guessContentTypeFromStream(
new BufferedInputStream(in));
in.close();
} catch (IOException e) {
// don't do anything
}
}
if (contentType == null) {
contentType = guessContentTypeFromName(entryName);
}
if (contentType == null) {
contentType = "content/unknown";
}
}
return contentType;
}
public String getHeaderField(String name) {
return jarFileURLConnection.getHeaderField(name);
}
/**
* Sets the general request property.
*
* @param key the keyword by which the request is known
* (e.g., "<code>accept</code>").
* @param value the value associated with it.
*/
public void setRequestProperty(String key, String value) {
jarFileURLConnection.setRequestProperty(key, value);
}
/**
* Returns the value of the named general request property for this
* connection.
*
* @return the value of the named general request property for this
* connection.
*/
public String getRequestProperty(String key) {
return jarFileURLConnection.getRequestProperty(key);
}
/**
* Adds a general request property specified by a
* key-value pair. This method will not overwrite
* existing values associated with the same key.
*
* @param key the keyword by which the request is known
* (e.g., "<code>accept</code>").
* @param value the value associated with it.
*/
public void addRequestProperty(String key, String value) {
jarFileURLConnection.addRequestProperty(key, value);
}
/**
* Returns an unmodifiable Map of general request
* properties for this connection. The Map keys
* are Strings that represent the request-header
* field names. Each Map value is a unmodifiable List
* of Strings that represents the corresponding
* field values.
*
* @return a Map of the general request properties for this connection.
*/
public Map<String,List<String>> getRequestProperties() {
return jarFileURLConnection.getRequestProperties();
}
/**
* Set the value of the <code>allowUserInteraction</code> field of
* this <code>URLConnection</code>.
*
* @param allowuserinteraction the new value.
* @see java.net.URLConnection#allowUserInteraction
*/
public void setAllowUserInteraction(boolean allowuserinteraction) {
jarFileURLConnection.setAllowUserInteraction(allowuserinteraction);
}
/**
* Returns the value of the <code>allowUserInteraction</code> field for
* this object.
*
* @return the value of the <code>allowUserInteraction</code> field for
* this object.
* @see java.net.URLConnection#allowUserInteraction
*/
public boolean getAllowUserInteraction() {
return jarFileURLConnection.getAllowUserInteraction();
}
/*
* cache control
*/
/**
* Sets the value of the <code>useCaches</code> field of this
* <code>URLConnection</code> to the specified value.
* <p>
* Some protocols do caching of documents. Occasionally, it is important
* to be able to "tunnel through" and ignore the caches (e.g., the
* "reload" button in a browser). If the UseCaches flag on a connection
* is true, the connection is allowed to use whatever caches it can.
* If false, caches are to be ignored.
* The default value comes from DefaultUseCaches, which defaults to
* true.
*
* @see java.net.URLConnection#useCaches
*/
public void setUseCaches(boolean usecaches) {
jarFileURLConnection.setUseCaches(usecaches);
}
/**
* Returns the value of this <code>URLConnection</code>'s
* <code>useCaches</code> field.
*
* @return the value of this <code>URLConnection</code>'s
* <code>useCaches</code> field.
* @see java.net.URLConnection#useCaches
*/
public boolean getUseCaches() {
return jarFileURLConnection.getUseCaches();
}
/**
* Sets the value of the <code>ifModifiedSince</code> field of
* this <code>URLConnection</code> to the specified value.
*
* @param ifmodifiedsince the new value.
* @see java.net.URLConnection#ifModifiedSince
*/
public void setIfModifiedSince(long ifmodifiedsince) {
jarFileURLConnection.setIfModifiedSince(ifmodifiedsince);
}
/**
* Sets the default value of the <code>useCaches</code> field to the
* specified value.
*
* @param defaultusecaches the new value.
* @see java.net.URLConnection#useCaches
*/
public void setDefaultUseCaches(boolean defaultusecaches) {
jarFileURLConnection.setDefaultUseCaches(defaultusecaches);
}
/**
* Returns the default value of a <code>URLConnection</code>'s
* <code>useCaches</code> flag.
* <p>
* Ths default is "sticky", being a part of the static state of all
* URLConnections. This flag applies to the next, and all following
* URLConnections that are created.
*
* @return the default value of a <code>URLConnection</code>'s
* <code>useCaches</code> flag.
* @see java.net.URLConnection#useCaches
*/
public boolean getDefaultUseCaches() {
return jarFileURLConnection.getDefaultUseCaches();
}
}

View file

@ -0,0 +1,284 @@
/*
* Copyright (c) 2001, 2016, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.jar;
import java.io.*;
import java.net.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.jar.*;
import java.util.zip.ZipFile;
import java.util.zip.ZipEntry;
import java.security.CodeSigner;
import java.security.cert.Certificate;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import sun.net.www.ParseUtil;
/* URL jar file is a common JarFile subtype used for JarURLConnection */
public class URLJarFile extends JarFile {
/*
* Interface to be able to call retrieve() in plugin if
* this variable is set.
*/
private static URLJarFileCallBack callback = null;
/* Controller of the Jar File's closing */
private URLJarFileCloseController closeController = null;
private static int BUF_SIZE = 2048;
private Manifest superMan;
private Attributes superAttr;
private Map<String, Attributes> superEntries;
static JarFile getJarFile(URL url) throws IOException {
return getJarFile(url, null);
}
static JarFile getJarFile(URL url, URLJarFileCloseController closeController) throws IOException {
if (isFileURL(url)) {
Runtime.Version version = "runtime".equals(url.getRef())
? JarFile.runtimeVersion()
: JarFile.baseVersion();
return new URLJarFile(url, closeController, version);
} else {
return retrieve(url, closeController);
}
}
/*
* Changed modifier from private to public in order to be able
* to instantiate URLJarFile from sun.plugin package.
*/
public URLJarFile(File file) throws IOException {
this(file, null);
}
/*
* Changed modifier from private to public in order to be able
* to instantiate URLJarFile from sun.plugin package.
*/
public URLJarFile(File file, URLJarFileCloseController closeController) throws IOException {
super(file, true, ZipFile.OPEN_READ | ZipFile.OPEN_DELETE);
this.closeController = closeController;
}
private URLJarFile(File file, URLJarFileCloseController closeController, Runtime.Version version)
throws IOException {
super(file, true, ZipFile.OPEN_READ | ZipFile.OPEN_DELETE, version);
this.closeController = closeController;
}
private URLJarFile(URL url, URLJarFileCloseController closeController, Runtime.Version version)
throws IOException {
super(new File(ParseUtil.decode(url.getFile())), true, ZipFile.OPEN_READ, version);
this.closeController = closeController;
}
private static boolean isFileURL(URL url) {
if (url.getProtocol().equalsIgnoreCase("file")) {
/*
* Consider this a 'file' only if it's a LOCAL file, because
* 'file:' URLs can be accessible through ftp.
*/
String host = url.getHost();
if (host == null || host.equals("") || host.equals("~") ||
host.equalsIgnoreCase("localhost"))
return true;
}
return false;
}
/**
* Returns the <code>ZipEntry</code> for the given entry name or
* <code>null</code> if not found.
*
* @param name the JAR file entry name
* @return the <code>ZipEntry</code> for the given entry name or
* <code>null</code> if not found
* @see java.util.zip.ZipEntry
*/
public ZipEntry getEntry(String name) {
ZipEntry ze = super.getEntry(name);
if (ze != null) {
if (ze instanceof JarEntry)
return new URLJarFileEntry((JarEntry)ze);
else
throw new InternalError(super.getClass() +
" returned unexpected entry type " +
ze.getClass());
}
return null;
}
public Manifest getManifest() throws IOException {
if (!isSuperMan()) {
return null;
}
Manifest man = new Manifest();
Attributes attr = man.getMainAttributes();
attr.putAll((Map)superAttr.clone());
// now deep copy the manifest entries
if (superEntries != null) {
Map<String, Attributes> entries = man.getEntries();
for (String key : superEntries.keySet()) {
Attributes at = superEntries.get(key);
entries.put(key, (Attributes) at.clone());
}
}
return man;
}
/* If close controller is set the notify the controller about the pending close */
public void close() throws IOException {
if (closeController != null) {
closeController.close(this);
}
super.close();
}
// optimal side-effects
private synchronized boolean isSuperMan() throws IOException {
if (superMan == null) {
superMan = super.getManifest();
}
if (superMan != null) {
superAttr = superMan.getMainAttributes();
superEntries = superMan.getEntries();
return true;
} else
return false;
}
/**
* Given a URL, retrieves a JAR file, caches it to disk, and creates a
* cached JAR file object.
*/
private static JarFile retrieve(final URL url, final URLJarFileCloseController closeController) throws IOException {
/*
* See if interface is set, then call retrieve function of the class
* that implements URLJarFileCallBack interface (sun.plugin - to
* handle the cache failure for JARJAR file.)
*/
if (callback != null)
{
return callback.retrieve(url);
}
else
{
JarFile result = null;
Runtime.Version version = "runtime".equals(url.getRef())
? JarFile.runtimeVersion()
: JarFile.baseVersion();
/* get the stream before asserting privileges */
try (final InputStream in = url.openConnection().getInputStream()) {
result = AccessController.doPrivileged(
new PrivilegedExceptionAction<>() {
public JarFile run() throws IOException {
Path tmpFile = Files.createTempFile("jar_cache", null);
try {
Files.copy(in, tmpFile, StandardCopyOption.REPLACE_EXISTING);
JarFile jarFile = new URLJarFile(tmpFile.toFile(), closeController, version);
tmpFile.toFile().deleteOnExit();
return jarFile;
} catch (Throwable thr) {
try {
Files.delete(tmpFile);
} catch (IOException ioe) {
thr.addSuppressed(ioe);
}
throw thr;
}
}
});
} catch (PrivilegedActionException pae) {
throw (IOException) pae.getException();
}
return result;
}
}
/*
* Set the call back interface to call retrive function in sun.plugin
* package if plugin is running.
*/
public static void setCallBack(URLJarFileCallBack cb)
{
callback = cb;
}
private class URLJarFileEntry extends JarEntry {
private JarEntry je;
URLJarFileEntry(JarEntry je) {
super(je);
this.je=je;
}
public Attributes getAttributes() throws IOException {
if (URLJarFile.this.isSuperMan()) {
Map<String, Attributes> e = URLJarFile.this.superEntries;
if (e != null) {
Attributes a = e.get(getName());
if (a != null)
return (Attributes)a.clone();
}
}
return null;
}
public java.security.cert.Certificate[] getCertificates() {
Certificate[] certs = je.getCertificates();
return certs == null? null: certs.clone();
}
public CodeSigner[] getCodeSigners() {
CodeSigner[] csg = je.getCodeSigners();
return csg == null? null: csg.clone();
}
}
public interface URLJarFileCloseController {
public void close(JarFile jarFile);
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2001, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.jar;
import java.io.*;
import java.net.*;
import java.util.jar.*;
/*
* This interface is used to call back to sun.plugin package.
*/
public interface URLJarFileCallBack
{
public JarFile retrieve (URL url) throws IOException;
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2014, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.jmod;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.net.MalformedURLException;
import java.io.IOException;
/**
* Placeholder protocol handler for the jmod protocol.
*/
public class Handler extends URLStreamHandler {
public Handler() { }
@Override
protected URLConnection openConnection(URL url) throws IOException {
String s = url.toString();
int index = s.indexOf("!/");
if (index == -1)
throw new MalformedURLException("no !/ found in url spec:" + s);
throw new IOException("Can't connect to jmod URL");
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2014, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.jrt;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
/**
* Protocol handler for accessing resources in the runtime image.
*/
public class Handler extends URLStreamHandler {
public Handler() { }
@Override
protected URLConnection openConnection(URL url) throws IOException {
return new JavaRuntimeURLConnection(url);
}
}

View file

@ -0,0 +1,186 @@
/*
* Copyright (c) 2014, 2017, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.jrt;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
import java.security.Permission;
import java.security.PrivilegedAction;
import jdk.internal.jimage.ImageLocation;
import jdk.internal.jimage.ImageReader;
import jdk.internal.jimage.ImageReaderFactory;
import jdk.internal.loader.URLClassPath;
import jdk.internal.loader.Resource;
import sun.net.www.ParseUtil;
import sun.net.www.URLConnection;
/**
* URLConnection implementation that can be used to connect to resources
* contained in the runtime image.
*/
public class JavaRuntimeURLConnection extends URLConnection {
// ImageReader to access resources in jimage
private static final ImageReader reader;
static {
PrivilegedAction<ImageReader> pa = ImageReaderFactory::getImageReader;
reader = AccessController.doPrivileged(pa);
}
// the module and resource name in the URL
private final String module;
private final String name;
// the Resource when connected
private volatile Resource resource;
JavaRuntimeURLConnection(URL url) throws IOException {
super(url);
String path = url.getPath();
if (path.length() == 0 || path.charAt(0) != '/')
throw new MalformedURLException(url + " missing path or /");
if (path.length() == 1) {
this.module = null;
this.name = null;
} else {
int pos = path.indexOf('/', 1);
if (pos == -1) {
this.module = path.substring(1);
this.name = null;
} else {
this.module = path.substring(1, pos);
this.name = ParseUtil.decode(path.substring(pos+1));
}
}
}
/**
* Finds a resource in a module, returning {@code null} if the resource
* is not found.
*/
private static Resource findResource(String module, String name) {
if (reader != null) {
URL url = toJrtURL(module, name);
ImageLocation location = reader.findLocation(module, name);
if (location != null && URLClassPath.checkURL(url) != null) {
return new Resource() {
@Override
public String getName() {
return name;
}
@Override
public URL getURL() {
return url;
}
@Override
public URL getCodeSourceURL() {
return toJrtURL(module);
}
@Override
public InputStream getInputStream() throws IOException {
byte[] resource = reader.getResource(location);
return new ByteArrayInputStream(resource);
}
@Override
public int getContentLength() {
long size = location.getUncompressedSize();
return (size > Integer.MAX_VALUE) ? -1 : (int) size;
}
};
}
}
return null;
}
@Override
public synchronized void connect() throws IOException {
if (!connected) {
if (name == null) {
String s = (module == null) ? "" : module;
throw new IOException("cannot connect to jrt:/" + s);
}
resource = findResource(module, name);
if (resource == null)
throw new IOException(module + "/" + name + " not found");
connected = true;
}
}
@Override
public InputStream getInputStream() throws IOException {
connect();
return resource.getInputStream();
}
@Override
public long getContentLengthLong() {
try {
connect();
return resource.getContentLength();
} catch (IOException ioe) {
return -1L;
}
}
@Override
public int getContentLength() {
long len = getContentLengthLong();
return len > Integer.MAX_VALUE ? -1 : (int)len;
}
@Override
public Permission getPermission() {
return new RuntimePermission("accessSystemModules");
}
/**
* Returns a jrt URL for the given module and resource name.
*/
private static URL toJrtURL(String module, String name) {
try {
return new URL("jrt:/" + module + "/" + name);
} catch (MalformedURLException e) {
throw new InternalError(e);
}
}
/**
* Returns a jrt URL for the given module.
*/
private static URL toJrtURL(String module) {
try {
return new URL("jrt:/" + module);
} catch (MalformedURLException e) {
throw new InternalError(e);
}
}
}

View file

@ -0,0 +1,158 @@
/*
* Copyright (c) 1995, 2011, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*-
* mailto stream opener
*/
package sun.net.www.protocol.mailto;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.io.*;
import sun.net.www.*;
//import sun.net.www.protocol.news.ArticlePoster;
import sun.net.smtp.SmtpClient;
/** open an nntp input stream given a URL */
public class Handler extends URLStreamHandler {
/*
// private String decodePercent(String s) {
// if (s==null || s.indexOf('%') < 0)
// return s;
// int limit = s.length();
// char d[] = new char[limit];
// int dp = 0;
// for (int sp = 0; sp < limit; sp++) {
// int c = s.charAt(sp);
// if (c == '%' && sp + 2 < limit) {
// int s1 = s.charAt(sp + 1);
// int s2 = s.charAt(sp + 2);
// if ('0' <= s1 && s1 <= '9')
// s1 = s1 - '0';
// else if ('a' <= s1 && s1 <= 'f')
// s1 = s1 - 'a' + 10;
// else if ('A' <= s1 && s1 <= 'F')
// s1 = s1 - 'A' + 10;
// else
// s1 = -1;
// if ('0' <= s2 && s2 <= '9')
// s2 = s2 - '0';
// else if ('a' <= s2 && s2 <= 'f')
// s2 = s2 - 'a' + 10;
// else if ('A' <= s2 && s2 <= 'F')
// s2 = s2 - 'A' + 10;
// else
// s2 = -1;
// if (s1 >= 0 && s2 >= 0) {
// c = (s1 << 4) | s2;
// sp += 2;
// }
// }
// d[dp++] = (char) c;
// }
// return new String(d, 0, dp);
// }
// public InputStream openStream(URL u) {
// String dest = u.file;
// String subj = "";
// int lastsl = dest.lastIndexOf('/');
// if (lastsl >= 0) {
// int st = dest.charAt(0) == '/' ? 1 : 0;
// if (lastsl > st)
// subj = dest.substring(st, lastsl);
// dest = dest.substring(lastsl + 1);
// }
// if (u.postData != null) {
// ArticlePoster.MailTo("Posted form",
// decodePercent(dest),
// u.postData);
// }
// else
// ArticlePoster.MailTo(decodePercent(subj), decodePercent(dest));
// return null;
// }
*/
public synchronized URLConnection openConnection(URL u) {
return new MailToURLConnection(u);
}
/**
* This method is called to parse the string spec into URL u for a
* mailto protocol.
*
* @param u the URL to receive the result of parsing the spec
* @param spec the URL string to parse
* @param start the character position to start parsing at. This is
* just past the ':'.
* @param limit the character position to stop parsing at.
*/
public void parseURL(URL u, String spec, int start, int limit) {
String protocol = u.getProtocol();
String host = "";
int port = u.getPort();
String file = "";
if (start < limit) {
file = spec.substring(start, limit);
}
/*
* Let's just make sure we DO have an Email address in the URL.
*/
boolean nogood = false;
if (file == null || file.equals(""))
nogood = true;
else {
boolean allwhites = true;
for (int i = 0; i < file.length(); i++)
if (!Character.isWhitespace(file.charAt(i)))
allwhites = false;
if (allwhites)
nogood = true;
}
if (nogood)
throw new RuntimeException("No email address");
setURLHandler(u, protocol, host, port, file, null);
}
/**
* This method is used to suppress the deprecated warning
*
* @param u the URL to receive the result of parsing the spec
* @param spec the URL string to parse
* @param start the character position to start parsing at. This is
* just past the ':'.
* @param limit the character position to stop parsing at.
*/
@SuppressWarnings("deprecation")
private void setURLHandler(URL u, String protocol, String host, int port, String file, String ref) {
setURL(u,protocol,host,port,file,null);
}
}

View file

@ -0,0 +1,140 @@
/*
* Copyright (c) 1996, 2008, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.mailto;
import java.net.URL;
import java.net.InetAddress;
import java.net.SocketPermission;
import java.io.*;
import java.security.Permission;
import sun.net.www.*;
import sun.net.smtp.SmtpClient;
import sun.net.www.ParseUtil;
/**
* Handle mailto URLs. To send mail using a mailto URLConnection,
* call <code>getOutputStream</code>, write the message to the output
* stream, and close it.
*
*/
public class MailToURLConnection extends URLConnection {
InputStream is = null;
OutputStream os = null;
SmtpClient client;
Permission permission;
private int connectTimeout = -1;
private int readTimeout = -1;
MailToURLConnection(URL u) {
super(u);
MessageHeader props = new MessageHeader();
props.add("content-type", "text/html");
setProperties(props);
}
/**
* Get the user's full email address - stolen from
* HotJavaApplet.getMailAddress().
*/
String getFromAddress() {
String str = System.getProperty("user.fromaddr");
if (str == null) {
str = System.getProperty("user.name");
if (str != null) {
String host = System.getProperty("mail.host");
if (host == null) {
try {
host = InetAddress.getLocalHost().getHostName();
} catch (java.net.UnknownHostException e) {
}
}
str += "@" + host;
} else {
str = "";
}
}
return str;
}
public void connect() throws IOException {
client = new SmtpClient(connectTimeout);
client.setReadTimeout(readTimeout);
}
@Override
public synchronized OutputStream getOutputStream() throws IOException {
if (os != null) {
return os;
} else if (is != null) {
throw new IOException("Cannot write output after reading input.");
}
connect();
String to = ParseUtil.decode(url.getPath());
client.from(getFromAddress());
client.to(to);
os = client.startMessage();
return os;
}
@Override
public Permission getPermission() throws IOException {
if (permission == null) {
connect();
String host = client.getMailHost() + ":" + 25;
permission = new SocketPermission(host, "connect");
}
return permission;
}
@Override
public void setConnectTimeout(int timeout) {
if (timeout < 0)
throw new IllegalArgumentException("timeouts can't be negative");
connectTimeout = timeout;
}
@Override
public int getConnectTimeout() {
return (connectTimeout < 0 ? 0 : connectTimeout);
}
@Override
public void setReadTimeout(int timeout) {
if (timeout < 0)
throw new IllegalArgumentException("timeouts can't be negative");
readTimeout = timeout;
}
@Override
public int getReadTimeout() {
return readTimeout < 0 ? 0 : readTimeout;
}
}