feat(jdk8): move files to new folder to avoid resources compiled.
This commit is contained in:
41
jdkSrc/jdk8/sun/net/www/ApplicationLaunchException.java
Normal file
41
jdkSrc/jdk8/sun/net/www/ApplicationLaunchException.java
Normal 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);
|
||||
}
|
||||
}
|
||||
277
jdkSrc/jdk8/sun/net/www/HeaderParser.java
Normal file
277
jdkSrc/jdk8/sun/net/www/HeaderParser.java
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2022, 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;
|
||||
import java.util.OptionalInt;
|
||||
|
||||
/* 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();
|
||||
StringBuffer sbuf = new StringBuffer();
|
||||
sbuf.append ("{size="+asize+" nkeys="+nkeys+" ");
|
||||
for (int i=0; k.hasNext(); i++) {
|
||||
String key = k.next();
|
||||
String val = findValue (i);
|
||||
if (val != null && "".equals (val)) {
|
||||
val = null;
|
||||
}
|
||||
sbuf.append (" {"+key+(val==null?"":","+val)+"}");
|
||||
if (k.hasNext()) {
|
||||
sbuf.append (",");
|
||||
}
|
||||
}
|
||||
sbuf.append (" }");
|
||||
return new String (sbuf);
|
||||
}
|
||||
|
||||
public int findInt(String k, int Default) {
|
||||
try {
|
||||
return Integer.parseInt(findValue(k, String.valueOf(Default)));
|
||||
} catch (Throwable t) {
|
||||
return Default;
|
||||
}
|
||||
}
|
||||
|
||||
public OptionalInt findInt(String k) {
|
||||
try {
|
||||
String s = findValue(k);
|
||||
if (s == null) {
|
||||
return OptionalInt.empty();
|
||||
}
|
||||
return OptionalInt.of(Integer.parseInt(s));
|
||||
} catch (Throwable t) {
|
||||
return OptionalInt.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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!");
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
607
jdkSrc/jdk8/sun/net/www/MessageHeader.java
Normal file
607
jdkSrc/jdk8/sun/net/www/MessageHeader.java
Normal file
@@ -0,0 +1,607 @@
|
||||
/*
|
||||
* 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.lang.reflect.Array;
|
||||
import java.net.ProtocolException;
|
||||
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;
|
||||
|
||||
// max number of bytes for headers, <=0 means unlimited;
|
||||
// this corresponds to the length of the names, plus the length
|
||||
// of the values, plus an overhead of 32 bytes per name: value
|
||||
// pair.
|
||||
// Note: we use the same definition as HTTP/2 SETTINGS_MAX_HEADER_LIST_SIZE
|
||||
// see RFC 9113, section 6.5.2.
|
||||
// https://www.rfc-editor.org/rfc/rfc9113.html#SETTINGS_MAX_HEADER_LIST_SIZE
|
||||
private final int maxHeaderSize;
|
||||
|
||||
// Aggregate size of the field lines (name + value + 32) x N
|
||||
// that have been parsed and accepted so far.
|
||||
// This is defined as a long to force promotion to long
|
||||
// and avoid overflows; see checkNewSize;
|
||||
private long size;
|
||||
|
||||
public MessageHeader () {
|
||||
this(0);
|
||||
}
|
||||
|
||||
public MessageHeader (int maxHeaderSize) {
|
||||
this.maxHeaderSize = maxHeaderSize;
|
||||
grow();
|
||||
}
|
||||
|
||||
public MessageHeader (InputStream is) throws java.io.IOException {
|
||||
maxHeaderSize = 0;
|
||||
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<String, List<String>>();
|
||||
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<String>();
|
||||
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<String>();
|
||||
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);
|
||||
}
|
||||
|
||||
/** Check if a line of message header looks like a request line.
|
||||
* This method does not perform a full validation but simply
|
||||
* returns false if the line does not end with 'HTTP/[1-9].[0-9]'
|
||||
* @param line the line to check.
|
||||
* @return true if the line might be a request line.
|
||||
*/
|
||||
private boolean isRequestline(String line) {
|
||||
String k = line.trim();
|
||||
int i = k.lastIndexOf(' ');
|
||||
if (i <= 0) return false;
|
||||
int len = k.length();
|
||||
if (len - i < 9) return false;
|
||||
|
||||
char c1 = k.charAt(len-3);
|
||||
char c2 = k.charAt(len-2);
|
||||
char c3 = k.charAt(len-1);
|
||||
if (c1 < '1' || c1 > '9') return false;
|
||||
if (c2 != '.') return false;
|
||||
if (c3 < '0' || c3 > '9') return false;
|
||||
|
||||
return (k.substring(i+1, len-3).equalsIgnoreCase("HTTP/"));
|
||||
}
|
||||
|
||||
|
||||
/** 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. Omits
|
||||
colon if key-value pair is the requestline. */
|
||||
public synchronized void print(PrintStream p) {
|
||||
for (int i = 0; i < nkeys; i++)
|
||||
if (keys[i] != null) {
|
||||
StringBuilder sb = new StringBuilder(keys[i]);
|
||||
if (values[i] != null) {
|
||||
sb.append(": " + values[i]);
|
||||
} else if (i != 0 || !isRequestline(keys[i])) {
|
||||
sb.append(":");
|
||||
}
|
||||
p.print(sb.append("\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 <>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;
|
||||
size = 0;
|
||||
}
|
||||
mergeHeader(is);
|
||||
}
|
||||
|
||||
private void checkMaxHeaderSize(int sz) throws ProtocolException {
|
||||
if (maxHeaderSize > 0) checkNewSize(size, sz, 0);
|
||||
}
|
||||
|
||||
private long checkNewSize(long size, int name, int value) throws ProtocolException {
|
||||
// See SETTINGS_MAX_HEADER_LIST_SIZE, RFC 9113, section 6.5.2.
|
||||
long newSize = size + name + value + 32;
|
||||
if (maxHeaderSize > 0 && newSize > maxHeaderSize) {
|
||||
Arrays.fill(keys, 0, nkeys, null);
|
||||
Arrays.fill(values,0, nkeys, null);
|
||||
nkeys = 0;
|
||||
throw new ProtocolException(String.format("Header size too big: %s > %s",
|
||||
newSize, maxHeaderSize));
|
||||
}
|
||||
return newSize;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
checkMaxHeaderSize(len);
|
||||
parseloop:{
|
||||
// We start parsing for a new name value pair here.
|
||||
// The max header size includes an overhead of 32 bytes per
|
||||
// name value pair.
|
||||
// See SETTINGS_MAX_HEADER_LIST_SIZE, RFC 9113, section 6.5.2.
|
||||
long maxRemaining = maxHeaderSize > 0
|
||||
? maxHeaderSize - size - 32
|
||||
: Long.MAX_VALUE;
|
||||
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;
|
||||
if (maxHeaderSize > 0 && len > maxRemaining) {
|
||||
checkMaxHeaderSize(len);
|
||||
}
|
||||
}
|
||||
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);
|
||||
int klen = k == null ? 0 : k.length();
|
||||
|
||||
size = checkNewSize(size, klen, v.length());
|
||||
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;
|
||||
}
|
||||
}
|
||||
217
jdkSrc/jdk8/sun/net/www/MeteredStream.java
Normal file
217
jdkSrc/jdk8/sun/net/www/MeteredStream.java
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright (c) 1994, 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;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
protected void finalize() throws Throwable {
|
||||
try {
|
||||
close();
|
||||
if (pi != null)
|
||||
pi.close();
|
||||
}
|
||||
finally {
|
||||
// Call super class
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
340
jdkSrc/jdk8/sun/net/www/MimeEntry.java
Normal file
340
jdkSrc/jdk8/sun/net/www/MimeEntry.java
Normal file
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Copyright (c) 1994, 2002, 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;
|
||||
|
||||
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() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
||||
String separator = "; ";
|
||||
boolean needSeparator = false;
|
||||
|
||||
int action = getAction();
|
||||
if (action != MimeEntry.UNKNOWN) {
|
||||
buf.append("action=" + actionKeywords[action]);
|
||||
needSeparator = true;
|
||||
}
|
||||
|
||||
String command = getLaunchString();
|
||||
if (command != null && command.length() > 0) {
|
||||
if (needSeparator) {
|
||||
buf.append(separator);
|
||||
}
|
||||
buf.append("application=" + command);
|
||||
needSeparator = true;
|
||||
}
|
||||
|
||||
if (getImageFileName() != null) {
|
||||
if (needSeparator) {
|
||||
buf.append(separator);
|
||||
}
|
||||
buf.append("icon=" + getImageFileName());
|
||||
needSeparator = true;
|
||||
}
|
||||
|
||||
String extensions = getExtensionsAsList();
|
||||
if (extensions.length() > 0) {
|
||||
if (needSeparator) {
|
||||
buf.append(separator);
|
||||
}
|
||||
buf.append("file_extensions=" + extensions);
|
||||
needSeparator = true;
|
||||
}
|
||||
|
||||
String description = getDescription();
|
||||
if (description != null && !description.equals(getType())) {
|
||||
if (needSeparator) {
|
||||
buf.append(separator);
|
||||
}
|
||||
buf.append("description=" + description);
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "MimeEntry[contentType=" + typeName
|
||||
+ ", image=" + imageFileName
|
||||
+ ", action=" + action
|
||||
+ ", command=" + command
|
||||
+ ", extensions=" + getExtensionsAsList()
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
205
jdkSrc/jdk8/sun/net/www/MimeLauncher.java
Normal file
205
jdkSrc/jdk8/sun/net/www/MimeLauncher.java
Normal 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;
|
||||
|
||||
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(threadName);
|
||||
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 = java.security.AccessController.doPrivileged(
|
||||
new sun.security.action.GetPropertyAction("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
|
||||
}
|
||||
}
|
||||
459
jdkSrc/jdk8/sun/net/www/MimeTable.java
Normal file
459
jdkSrc/jdk8/sun/net/www/MimeTable.java
Normal file
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
* 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;
|
||||
try {
|
||||
InputStream is;
|
||||
// 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);
|
||||
if (!file.exists()) {
|
||||
// No user-table, try to load the default built-in table.
|
||||
file = new File(System.getProperty("java.home") +
|
||||
File.separator +
|
||||
"lib" +
|
||||
File.separator +
|
||||
"content-types.properties");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No user table, try to load the default built-in table.
|
||||
file = new File(System.getProperty("java.home") +
|
||||
File.separator +
|
||||
"lib" +
|
||||
File.separator +
|
||||
"content-types.properties");
|
||||
}
|
||||
|
||||
is = new BufferedInputStream(new FileInputStream(file));
|
||||
entries.load(is);
|
||||
is.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
System.err.println("Warning: default mime table not found: " +
|
||||
file.getPath());
|
||||
return;
|
||||
}
|
||||
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 synchronized boolean save(String filename) {
|
||||
if (filename == null) {
|
||||
filename = System.getProperty("user.home" +
|
||||
File.separator +
|
||||
"lib" +
|
||||
File.separator +
|
||||
"content-types.properties");
|
||||
}
|
||||
|
||||
return saveAsProperties(new File(filename));
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
*/
|
||||
}
|
||||
680
jdkSrc/jdk8/sun/net/www/ParseUtil.java
Normal file
680
jdkSrc/jdk8/sun/net/www/ParseUtil.java
Normal file
@@ -0,0 +1,680 @@
|
||||
/*
|
||||
* 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.util.BitSet;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
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 {
|
||||
static BitSet encodedInPath;
|
||||
|
||||
static {
|
||||
encodedInPath = new BitSet(256);
|
||||
|
||||
// Set the bits corresponding to 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.
|
||||
encodedInPath.set('=');
|
||||
encodedInPath.set(';');
|
||||
encodedInPath.set('?');
|
||||
encodedInPath.set('/');
|
||||
|
||||
// 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.
|
||||
encodedInPath.set('#');
|
||||
encodedInPath.set(' ');
|
||||
encodedInPath.set('<');
|
||||
encodedInPath.set('>');
|
||||
encodedInPath.set('%');
|
||||
encodedInPath.set('"');
|
||||
encodedInPath.set('{');
|
||||
encodedInPath.set('}');
|
||||
encodedInPath.set('|');
|
||||
encodedInPath.set('\\');
|
||||
encodedInPath.set('^');
|
||||
encodedInPath.set('[');
|
||||
encodedInPath.set(']');
|
||||
encodedInPath.set('`');
|
||||
|
||||
// US ASCII control characters 00-1F and 7F.
|
||||
for (int i=0; i<32; i++)
|
||||
encodedInPath.set(i);
|
||||
encodedInPath.set(127);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
char[] retCC = new char[path.length() * 2 + 16];
|
||||
int retLen = 0;
|
||||
char[] pathCC = path.toCharArray();
|
||||
|
||||
int n = path.length();
|
||||
for (int i=0; i<n; i++) {
|
||||
char c = pathCC[i];
|
||||
if ((!flag && c == '/') || (flag && c == File.separatorChar))
|
||||
retCC[retLen++] = '/';
|
||||
else {
|
||||
if (c <= 0x007F) {
|
||||
if (c >= 'a' && c <= 'z' ||
|
||||
c >= 'A' && c <= 'Z' ||
|
||||
c >= '0' && c <= '9') {
|
||||
retCC[retLen++] = c;
|
||||
} else
|
||||
if (encodedInPath.get(c))
|
||||
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.substring(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.substring(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.substring(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 final static 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 --
|
||||
|
||||
// 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 = 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 = 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 = 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 = 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 = lowMask("-_.!~*'()");
|
||||
private static final long H_MARK = 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 = lowMask(";/?:@&=+$,[]");
|
||||
private static final long H_RESERVED = 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;
|
||||
|
||||
// Dash, for use in domainlabel and toplabel
|
||||
private static final long L_DASH = lowMask("-");
|
||||
private static final long H_DASH = highMask("-");
|
||||
|
||||
// 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 | lowMask(":@&=+$,");
|
||||
private static final long H_PCHAR
|
||||
= H_UNRESERVED | H_ESCAPED | highMask(":@&=+$,");
|
||||
|
||||
// All valid path characters
|
||||
private static final long L_PATH = L_PCHAR | lowMask(";/");
|
||||
private static final long H_PATH = H_PCHAR | highMask(";/");
|
||||
|
||||
// userinfo = *( unreserved | escaped |
|
||||
// ";" | ":" | "&" | "=" | "+" | "$" | "," )
|
||||
private static final long L_USERINFO
|
||||
= L_UNRESERVED | L_ESCAPED | lowMask(";:&=+$,");
|
||||
private static final long H_USERINFO
|
||||
= H_UNRESERVED | H_ESCAPED | highMask(";:&=+$,");
|
||||
|
||||
// reg_name = 1*( unreserved | escaped | "$" | "," |
|
||||
// ";" | ":" | "@" | "&" | "=" | "+" )
|
||||
private static final long L_REG_NAME
|
||||
= L_UNRESERVED | L_ESCAPED | lowMask("$,;:@&=+");
|
||||
private static final long H_REG_NAME
|
||||
= H_UNRESERVED | H_ESCAPED | highMask("$,;:@&=+");
|
||||
|
||||
// All valid characters for server-based authorities
|
||||
private static final long L_SERVER
|
||||
= L_USERINFO | L_ALPHANUM | L_DASH | lowMask(".:@[]");
|
||||
private static final long H_SERVER
|
||||
= H_USERINFO | H_ALPHANUM | H_DASH | highMask(".:@[]");
|
||||
}
|
||||
246
jdkSrc/jdk8/sun/net/www/URLConnection.java
Normal file
246
jdkSrc/jdk8/sun/net/www/URLConnection.java
Normal 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
|
||||
*/
|
||||
|
||||
abstract public 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 synchronized static void setProxiedHost(String host) {
|
||||
proxiedHosts.put(host.toLowerCase(), null);
|
||||
}
|
||||
|
||||
public synchronized static boolean isProxiedHost(String host) {
|
||||
return proxiedHosts.containsKey(host.toLowerCase());
|
||||
}
|
||||
}
|
||||
43
jdkSrc/jdk8/sun/net/www/content/audio/aiff.java
Normal file
43
jdkSrc/jdk8/sun/net/www/content/audio/aiff.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Basic .aiff audio handler.
|
||||
* @author Jeff Nisewanger
|
||||
*/
|
||||
package sun.net.www.content.audio;
|
||||
|
||||
import java.net.*;
|
||||
import java.io.IOException;
|
||||
import sun.applet.AppletAudioClip;
|
||||
|
||||
/**
|
||||
* Returns an AppletAudioClip object.
|
||||
*/
|
||||
public class aiff extends ContentHandler {
|
||||
public Object getContent(URLConnection uc) throws IOException {
|
||||
return new AppletAudioClip(uc);
|
||||
}
|
||||
}
|
||||
45
jdkSrc/jdk8/sun/net/www/content/audio/basic.java
Normal file
45
jdkSrc/jdk8/sun/net/www/content/audio/basic.java
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Basic .au and .snd audio handler.
|
||||
* @author Jeff Nisewanger
|
||||
*/
|
||||
package sun.net.www.content.audio;
|
||||
|
||||
import java.net.*;
|
||||
import java.io.IOException;
|
||||
import sun.applet.AppletAudioClip;
|
||||
|
||||
/**
|
||||
* Returns an AppletAudioClip object.
|
||||
* This provides backwards compatibility with the behavior
|
||||
* of ClassLoader.getResource().getContent() on JDK1.1.
|
||||
*/
|
||||
public class basic extends ContentHandler {
|
||||
public Object getContent(URLConnection uc) throws IOException {
|
||||
return new AppletAudioClip(uc);
|
||||
}
|
||||
}
|
||||
43
jdkSrc/jdk8/sun/net/www/content/audio/wav.java
Normal file
43
jdkSrc/jdk8/sun/net/www/content/audio/wav.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Basic .wav audio handler.
|
||||
* @author Jeff Nisewanger
|
||||
*/
|
||||
package sun.net.www.content.audio;
|
||||
|
||||
import java.net.*;
|
||||
import java.io.IOException;
|
||||
import sun.applet.AppletAudioClip;
|
||||
|
||||
/**
|
||||
* Returns an AppletAudioClip object.
|
||||
*/
|
||||
public class wav extends ContentHandler {
|
||||
public Object getContent(URLConnection uc) throws IOException {
|
||||
return new AppletAudioClip(uc);
|
||||
}
|
||||
}
|
||||
43
jdkSrc/jdk8/sun/net/www/content/audio/x_aiff.java
Normal file
43
jdkSrc/jdk8/sun/net/www/content/audio/x_aiff.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Basic .aiff audio handler.
|
||||
* @author Jeff Nisewanger
|
||||
*/
|
||||
package sun.net.www.content.audio;
|
||||
|
||||
import java.net.*;
|
||||
import java.io.IOException;
|
||||
import sun.applet.AppletAudioClip;
|
||||
|
||||
/**
|
||||
* Returns an AppletAudioClip object.
|
||||
*/
|
||||
public class x_aiff extends ContentHandler {
|
||||
public Object getContent(URLConnection uc) throws IOException {
|
||||
return new AppletAudioClip(uc);
|
||||
}
|
||||
}
|
||||
43
jdkSrc/jdk8/sun/net/www/content/audio/x_wav.java
Normal file
43
jdkSrc/jdk8/sun/net/www/content/audio/x_wav.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Basic .wav audio handler.
|
||||
* @author Jeff Nisewanger
|
||||
*/
|
||||
package sun.net.www.content.audio;
|
||||
|
||||
import java.net.*;
|
||||
import java.io.IOException;
|
||||
import sun.applet.AppletAudioClip;
|
||||
|
||||
/**
|
||||
* Returns an AppletAudioClip object.
|
||||
*/
|
||||
public class x_wav extends ContentHandler {
|
||||
public Object getContent(URLConnection uc) throws IOException {
|
||||
return new AppletAudioClip(uc);
|
||||
}
|
||||
}
|
||||
54
jdkSrc/jdk8/sun/net/www/content/image/gif.java
Normal file
54
jdkSrc/jdk8/sun/net/www/content/image/gif.java
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 1994, 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.content.image;
|
||||
|
||||
import java.net.*;
|
||||
import sun.awt.image.*;
|
||||
import java.io.IOException;
|
||||
import java.awt.Image;
|
||||
import java.awt.Toolkit;
|
||||
|
||||
|
||||
public class gif extends ContentHandler {
|
||||
public Object getContent(URLConnection urlc) throws java.io.IOException {
|
||||
return new URLImageSource(urlc);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Object getContent(URLConnection urlc, Class[] classes) throws IOException {
|
||||
Class<?>[] cls = classes;
|
||||
for (int i = 0; i < cls.length; i++) {
|
||||
if (cls[i].isAssignableFrom(URLImageSource.class)) {
|
||||
return new URLImageSource(urlc);
|
||||
}
|
||||
if (cls[i].isAssignableFrom(Image.class)) {
|
||||
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||
return tk.createImage(new URLImageSource(urlc));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
53
jdkSrc/jdk8/sun/net/www/content/image/jpeg.java
Normal file
53
jdkSrc/jdk8/sun/net/www/content/image/jpeg.java
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package sun.net.www.content.image;
|
||||
|
||||
import java.net.*;
|
||||
import sun.awt.image.*;
|
||||
import java.io.IOException;
|
||||
import java.awt.Image;
|
||||
import java.awt.Toolkit;
|
||||
|
||||
public class jpeg extends ContentHandler {
|
||||
public Object getContent(URLConnection urlc) throws java.io.IOException {
|
||||
return new URLImageSource(urlc);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Object getContent(URLConnection urlc, Class[] classes) throws IOException {
|
||||
Class<?>[] cls = classes;
|
||||
for (int i = 0; i < cls.length; i++) {
|
||||
if (cls[i].isAssignableFrom(URLImageSource.class)) {
|
||||
return new URLImageSource(urlc);
|
||||
}
|
||||
if (cls[i].isAssignableFrom(Image.class)) {
|
||||
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||
return tk.createImage(new URLImageSource(urlc));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
53
jdkSrc/jdk8/sun/net/www/content/image/png.java
Normal file
53
jdkSrc/jdk8/sun/net/www/content/image/png.java
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.content.image;
|
||||
|
||||
import java.net.*;
|
||||
import java.io.IOException;
|
||||
import sun.awt.image.*;
|
||||
import java.awt.Image;
|
||||
import java.awt.Toolkit;
|
||||
|
||||
public class png extends ContentHandler {
|
||||
public Object getContent(URLConnection urlc) throws java.io.IOException {
|
||||
return new URLImageSource(urlc);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Object getContent(URLConnection urlc, Class[] classes) throws IOException {
|
||||
Class<?>[] cls = classes;
|
||||
for (int i = 0; i < cls.length; i++) {
|
||||
if (cls[i].isAssignableFrom(URLImageSource.class)) {
|
||||
return new URLImageSource(urlc);
|
||||
}
|
||||
if (cls[i].isAssignableFrom(Image.class)) {
|
||||
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||
return tk.createImage(new URLImageSource(urlc));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
52
jdkSrc/jdk8/sun/net/www/content/image/x_xbitmap.java
Normal file
52
jdkSrc/jdk8/sun/net/www/content/image/x_xbitmap.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 1994, 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.content.image;
|
||||
|
||||
import java.net.*;
|
||||
import sun.awt.image.*;
|
||||
import java.awt.Image;
|
||||
import java.awt.Toolkit;
|
||||
|
||||
public class x_xbitmap extends ContentHandler {
|
||||
public Object getContent(URLConnection urlc) throws java.io.IOException {
|
||||
return new URLImageSource(urlc);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Object getContent(URLConnection urlc, Class[] classes) throws java.io.IOException {
|
||||
Class<?>[] cls = classes;
|
||||
for (int i = 0; i < cls.length; i++) {
|
||||
if (cls[i].isAssignableFrom(URLImageSource.class)) {
|
||||
return new URLImageSource(urlc);
|
||||
}
|
||||
if (cls[i].isAssignableFrom(Image.class)) {
|
||||
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||
return tk.createImage(new URLImageSource(urlc));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
52
jdkSrc/jdk8/sun/net/www/content/image/x_xpixmap.java
Normal file
52
jdkSrc/jdk8/sun/net/www/content/image/x_xpixmap.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 1994, 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.content.image;
|
||||
|
||||
import java.net.*;
|
||||
import sun.awt.image.*;
|
||||
import java.awt.Image;
|
||||
import java.awt.Toolkit;
|
||||
|
||||
public class x_xpixmap extends ContentHandler {
|
||||
public Object getContent(URLConnection urlc) throws java.io.IOException {
|
||||
return new URLImageSource(urlc);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Object getContent(URLConnection urlc, Class[] classes) throws java.io.IOException {
|
||||
Class<?>[] cls = classes;
|
||||
for (int i = 0; i < cls.length; i++) {
|
||||
if (cls[i].isAssignableFrom(URLImageSource.class)) {
|
||||
return new URLImageSource(urlc);
|
||||
}
|
||||
if (cls[i].isAssignableFrom(Image.class)) {
|
||||
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||
return tk.createImage(new URLImageSource(urlc));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
33
jdkSrc/jdk8/sun/net/www/content/text/Generic.java
Normal file
33
jdkSrc/jdk8/sun/net/www/content/text/Generic.java
Normal 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! */
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
48
jdkSrc/jdk8/sun/net/www/content/text/plain.java
Normal file
48
jdkSrc/jdk8/sun/net/www/content/text/plain.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
780
jdkSrc/jdk8/sun/net/www/http/ChunkedInputStream.java
Normal file
780
jdkSrc/jdk8/sun/net/www/http/ChunkedInputStream.java
Normal 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 final static 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.substring(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);
|
||||
}
|
||||
|
||||
}
|
||||
300
jdkSrc/jdk8/sun/net/www/http/ChunkedOutputStream.java
Normal file
300
jdkSrc/jdk8/sun/net/www/http/ChunkedOutputStream.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
171
jdkSrc/jdk8/sun/net/www/http/HttpCapture.java
Normal file
171
jdkSrc/jdk8/sun/net/www/http/HttpCapture.java
Normal 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 = null;
|
||||
private boolean incoming = true;
|
||||
private BufferedWriter out = null;
|
||||
private static boolean initialized = false;
|
||||
private static volatile ArrayList<Pattern> patterns = null;
|
||||
private static volatile ArrayList<String> capFiles = null;
|
||||
|
||||
private static synchronized void init() {
|
||||
initialized = true;
|
||||
String rulesFile = java.security.AccessController.doPrivileged(
|
||||
new java.security.PrivilegedAction<String>() {
|
||||
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<Pattern>();
|
||||
capFiles = new ArrayList<String>();
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
76
jdkSrc/jdk8/sun/net/www/http/HttpCaptureInputStream.java
Normal file
76
jdkSrc/jdk8/sun/net/www/http/HttpCaptureInputStream.java
Normal 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;
|
||||
}
|
||||
}
|
||||
73
jdkSrc/jdk8/sun/net/www/http/HttpCaptureOutputStream.java
Normal file
73
jdkSrc/jdk8/sun/net/www/http/HttpCaptureOutputStream.java
Normal 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();
|
||||
}
|
||||
}
|
||||
1104
jdkSrc/jdk8/sun/net/www/http/HttpClient.java
Normal file
1104
jdkSrc/jdk8/sun/net/www/http/HttpClient.java
Normal file
File diff suppressed because it is too large
Load Diff
40
jdkSrc/jdk8/sun/net/www/http/Hurryable.java
Normal file
40
jdkSrc/jdk8/sun/net/www/http/Hurryable.java
Normal 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();
|
||||
|
||||
}
|
||||
426
jdkSrc/jdk8/sun/net/www/http/KeepAliveCache.java
Normal file
426
jdkSrc/jdk8/sun/net/www/http/KeepAliveCache.java
Normal file
@@ -0,0 +1,426 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 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.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.NotSerializableException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.net.URL;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import sun.security.action.GetIntegerAction;
|
||||
import sun.net.www.protocol.http.HttpURLConnection;
|
||||
import sun.util.logging.PlatformLogger;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
// Keep alive time set according to priority specified here:
|
||||
// 1. If server specifies a time with a Keep-Alive header
|
||||
// 2. If user specifies a time with system property below
|
||||
// 3. Default values which depend on proxy vs server and whether
|
||||
// a Connection: keep-alive header was sent by server
|
||||
|
||||
// name suffixed with "server" or "proxy"
|
||||
private static final String keepAliveProp = "http.keepAlive.time.";
|
||||
|
||||
private static final int userKeepAliveServer;
|
||||
private static final int userKeepAliveProxy;
|
||||
|
||||
static final PlatformLogger logger = HttpURLConnection.getHttpLogger();
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
static int getUserKeepAliveSeconds(String type) {
|
||||
int v = AccessController.doPrivileged(
|
||||
new GetIntegerAction(keepAliveProp+type, -1)).intValue();
|
||||
return v < -1 ? -1 : v;
|
||||
}
|
||||
|
||||
static {
|
||||
userKeepAliveServer = getUserKeepAliveSeconds("server");
|
||||
userKeepAliveProxy = getUserKeepAliveSeconds("proxy");
|
||||
}
|
||||
|
||||
/* 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 = AccessController.doPrivileged(
|
||||
new 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 void put(final URL url, Object obj, HttpClient http) {
|
||||
// this method may need to close an HttpClient, either because
|
||||
// it is not cacheable, or because the cache is at its capacity.
|
||||
// In the latter case, we close the least recently used client.
|
||||
// The client to close is stored in oldClient, and is closed
|
||||
// after cacheLock is released.
|
||||
HttpClient oldClient = null;
|
||||
synchronized (this) {
|
||||
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;
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
public Void run() {
|
||||
// We want to create the Keep-Alive-Timer in the
|
||||
// system threadgroup
|
||||
ThreadGroup grp = Thread.currentThread().getThreadGroup();
|
||||
ThreadGroup parent = null;
|
||||
while ((parent = grp.getParent()) != null) {
|
||||
grp = parent;
|
||||
}
|
||||
|
||||
keepAliveTimer = new Thread(grp, cache, "Keep-Alive-Timer");
|
||||
keepAliveTimer.setDaemon(true);
|
||||
keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2);
|
||||
// Set the context class loader to null in order to avoid
|
||||
// keeping a strong reference to an application classloader.
|
||||
keepAliveTimer.setContextClassLoader(null);
|
||||
keepAliveTimer.start();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
KeepAliveKey key = new KeepAliveKey(url, obj);
|
||||
ClientVector v = super.get(key);
|
||||
|
||||
if (v == null) {
|
||||
int keepAliveTimeout = http.getKeepAliveTimeout();
|
||||
if (keepAliveTimeout == 0) {
|
||||
keepAliveTimeout = getUserKeepAlive(http.getUsingProxy());
|
||||
if (keepAliveTimeout == -1) {
|
||||
// same default for server and proxy
|
||||
keepAliveTimeout = 5;
|
||||
}
|
||||
} else if (keepAliveTimeout == -1) {
|
||||
keepAliveTimeout = getUserKeepAlive(http.getUsingProxy());
|
||||
if (keepAliveTimeout == -1) {
|
||||
// different default for server and proxy
|
||||
keepAliveTimeout = http.getUsingProxy() ? 60 : 5;
|
||||
}
|
||||
} else if (keepAliveTimeout == -2) {
|
||||
keepAliveTimeout = 0;
|
||||
}
|
||||
// at this point keepAliveTimeout is the number of seconds to keep
|
||||
// alive, which could be 0, if the user specified 0 for the property
|
||||
assert keepAliveTimeout >= 0;
|
||||
if (keepAliveTimeout == 0) {
|
||||
oldClient = http;
|
||||
} else {
|
||||
v = new ClientVector(keepAliveTimeout * 1000);
|
||||
v.put(http);
|
||||
super.put(key, v);
|
||||
}
|
||||
} else {
|
||||
oldClient = v.put(http);
|
||||
}
|
||||
}
|
||||
// close after releasing locks
|
||||
if (oldClient != null) {
|
||||
oldClient.closeServer();
|
||||
}
|
||||
}
|
||||
|
||||
// returns the keep alive set by user in system property or -1 if not set
|
||||
private static int getUserKeepAlive(boolean isProxy) {
|
||||
return isProxy ? userKeepAliveProxy : userKeepAliveServer;
|
||||
}
|
||||
|
||||
/* 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.isEmpty()) {
|
||||
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) {}
|
||||
List<HttpClient> closeList = null;
|
||||
|
||||
// Remove all outdated HttpClients.
|
||||
synchronized (this) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
List<KeepAliveKey> keysToRemove = new ArrayList<>();
|
||||
|
||||
for (KeepAliveKey key : keySet()) {
|
||||
ClientVector v = get(key);
|
||||
synchronized (v) {
|
||||
KeepAliveEntry e = v.peekLast();
|
||||
while (e != null) {
|
||||
if ((currentTime - e.idleStartTime) > v.nap) {
|
||||
v.pollLast();
|
||||
if (closeList == null) {
|
||||
closeList = new ArrayList<>();
|
||||
}
|
||||
closeList.add(e.hc);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
e = v.peekLast();
|
||||
}
|
||||
|
||||
if (v.isEmpty()) {
|
||||
keysToRemove.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (KeepAliveKey key : keysToRemove) {
|
||||
removeVector(key);
|
||||
}
|
||||
}
|
||||
// close connections outside cacheLock
|
||||
if (closeList != null) {
|
||||
for (HttpClient hc : closeList) {
|
||||
hc.closeServer();
|
||||
}
|
||||
}
|
||||
} while (!isEmpty());
|
||||
}
|
||||
|
||||
/*
|
||||
* Do not serialize this class!
|
||||
*/
|
||||
private void writeObject(ObjectOutputStream stream) throws IOException {
|
||||
throw new NotSerializableException();
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream stream)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
throw new NotSerializableException();
|
||||
}
|
||||
}
|
||||
|
||||
/* LIFO order for reusing HttpClients. Most recent entries at the front.
|
||||
* If > maxConns are in use, discard oldest.
|
||||
*/
|
||||
class ClientVector extends ArrayDeque<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() {
|
||||
// check the most recent connection, use if still valid
|
||||
KeepAliveEntry e = peekFirst();
|
||||
if (e == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if ((currentTime - e.idleStartTime) > nap) {
|
||||
return null; // all connections stale - will be cleaned up later
|
||||
} else {
|
||||
pollFirst();
|
||||
if (KeepAliveCache.logger.isLoggable(PlatformLogger.Level.FINEST)) {
|
||||
String msg = "cached HttpClient was idle for "
|
||||
+ Long.toString(currentTime - e.idleStartTime);
|
||||
KeepAliveCache.logger.finest(msg);
|
||||
}
|
||||
return e.hc;
|
||||
}
|
||||
}
|
||||
|
||||
/* return a still valid, unused HttpClient */
|
||||
synchronized HttpClient put(HttpClient h) {
|
||||
HttpClient staleClient = null;
|
||||
assert KeepAliveCache.getMaxConnections() > 0;
|
||||
if (size() >= KeepAliveCache.getMaxConnections()) {
|
||||
// remove oldest connection
|
||||
staleClient = removeLast().hc;
|
||||
}
|
||||
addFirst(new KeepAliveEntry(h, System.currentTimeMillis()));
|
||||
// close after releasing the locks
|
||||
return staleClient;
|
||||
}
|
||||
|
||||
/* remove an HttpClient */
|
||||
synchronized boolean remove(HttpClient h) {
|
||||
for (KeepAliveEntry curr : this) {
|
||||
if (curr.hc == h) {
|
||||
return super.remove(curr);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do not serialize this class!
|
||||
*/
|
||||
private void writeObject(ObjectOutputStream stream) throws IOException {
|
||||
throw new NotSerializableException();
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream stream)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
throw new NotSerializableException();
|
||||
}
|
||||
}
|
||||
|
||||
class KeepAliveKey {
|
||||
private final String protocol;
|
||||
private final String host;
|
||||
private final int port;
|
||||
private final Object obj; // 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 {
|
||||
final HttpClient hc;
|
||||
final long idleStartTime;
|
||||
|
||||
KeepAliveEntry(HttpClient hc, long idleStartTime) {
|
||||
this.hc = hc;
|
||||
this.idleStartTime = idleStartTime;
|
||||
}
|
||||
}
|
||||
56
jdkSrc/jdk8/sun/net/www/http/KeepAliveCleanerEntry.java
Normal file
56
jdkSrc/jdk8/sun/net/www/http/KeepAliveCleanerEntry.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
205
jdkSrc/jdk8/sun/net/www/http/KeepAliveStream.java
Normal file
205
jdkSrc/jdk8/sun/net/www/http/KeepAliveStream.java
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
// We want to create the Keep-Alive-SocketCleaner in the
|
||||
// system threadgroup
|
||||
ThreadGroup grp = Thread.currentThread().getThreadGroup();
|
||||
ThreadGroup parent = null;
|
||||
while ((parent = grp.getParent()) != null) {
|
||||
grp = parent;
|
||||
}
|
||||
|
||||
cleanerThread = new Thread(grp, queue, "Keep-Alive-SocketCleaner");
|
||||
cleanerThread.setDaemon(true);
|
||||
cleanerThread.setPriority(Thread.MAX_PRIORITY - 2);
|
||||
// Set the context class loader to null in order to avoid
|
||||
// keeping a strong reference to an application classloader.
|
||||
cleanerThread.setContextClassLoader(null);
|
||||
cleanerThread.start();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
} // queue
|
||||
}
|
||||
|
||||
protected long remainingToRead() {
|
||||
return expected - count;
|
||||
}
|
||||
|
||||
protected void setClosed() {
|
||||
in = null;
|
||||
hc = null;
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
155
jdkSrc/jdk8/sun/net/www/http/KeepAliveStreamCleaner.java
Normal file
155
jdkSrc/jdk8/sun/net/www/http/KeepAliveStreamCleaner.java
Normal 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);
|
||||
}
|
||||
}
|
||||
105
jdkSrc/jdk8/sun/net/www/http/PosterOutputStream.java
Normal file
105
jdkSrc/jdk8/sun/net/www/http/PosterOutputStream.java
Normal 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 ();
|
||||
}
|
||||
}
|
||||
233
jdkSrc/jdk8/sun/net/www/protocol/file/FileURLConnection.java
Normal file
233
jdkSrc/jdk8/sun/net/www/protocol/file/FileURLConnection.java
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import sun.security.action.GetPropertyAction;
|
||||
import sun.security.action.GetIntegerAction;
|
||||
import sun.security.action.GetBooleanAction;
|
||||
|
||||
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();
|
||||
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
||||
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);
|
||||
buf.append(fileName);
|
||||
buf.append("\n");
|
||||
}
|
||||
// Put it into a (default) locale-specific byte-stream.
|
||||
is = new ByteArrayInputStream(buf.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 {
|
||||
permission = new FilePermission(
|
||||
decodedPath.replace('/',File.separatorChar), "read");
|
||||
}
|
||||
}
|
||||
return permission;
|
||||
}
|
||||
}
|
||||
154
jdkSrc/jdk8/sun/net/www/protocol/file/Handler.java
Normal file
154
jdkSrc/jdk8/sun/net/www/protocol/file/Handler.java
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. 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.file;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.Proxy;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URLStreamHandler;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import sun.net.www.ParseUtil;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Open an file input stream given a URL.
|
||||
* @author James Gosling
|
||||
*/
|
||||
public class Handler extends URLStreamHandler {
|
||||
|
||||
private String getHost(URL url) {
|
||||
String host = url.getHost();
|
||||
if (host == null)
|
||||
host = "";
|
||||
return host;
|
||||
}
|
||||
|
||||
|
||||
protected void parseURL(URL u, String spec, int start, int limit) {
|
||||
/*
|
||||
* Ugly backwards compatibility. Flip any file separator
|
||||
* characters to be forward slashes. This is a nop on Unix
|
||||
* and "fixes" win32 file paths. According to RFC 2396,
|
||||
* only forward slashes may be used to represent hierarchy
|
||||
* separation in a URL but previous releases unfortunately
|
||||
* performed this "fixup" behavior in the file URL parsing code
|
||||
* rather than forcing this to be fixed in the caller of the URL
|
||||
* class where it belongs. Since backslash is an "unwise"
|
||||
* character that would normally be encoded if literally intended
|
||||
* as a non-seperator character the damage of veering away from the
|
||||
* specification is presumably limited.
|
||||
*/
|
||||
super.parseURL(u, spec.replace(File.separatorChar, '/'), start, limit);
|
||||
}
|
||||
|
||||
public synchronized URLConnection openConnection(URL url)
|
||||
throws IOException {
|
||||
return openConnection(url, null);
|
||||
}
|
||||
|
||||
public synchronized URLConnection openConnection(URL url, Proxy p)
|
||||
throws IOException {
|
||||
|
||||
String path;
|
||||
String file = url.getFile();
|
||||
String host = url.getHost();
|
||||
|
||||
path = ParseUtil.decode(file);
|
||||
path = path.replace('/', '\\');
|
||||
path = path.replace('|', ':');
|
||||
|
||||
if ((host == null) || host.equals("") ||
|
||||
host.equalsIgnoreCase("localhost") ||
|
||||
host.equals("~")) {
|
||||
return createFileURLConnection(url, new File(path));
|
||||
}
|
||||
|
||||
/*
|
||||
* attempt to treat this as a UNC path. See 4180841
|
||||
*/
|
||||
path = "\\\\" + host + path;
|
||||
File f = new File(path);
|
||||
if (f.exists()) {
|
||||
return new UNCFileURLConnection(url, f, path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now attempt an ftp connection.
|
||||
*/
|
||||
URLConnection uc;
|
||||
URL newurl;
|
||||
|
||||
try {
|
||||
newurl = new URL("ftp", host, file +
|
||||
(url.getRef() == null ? "":
|
||||
"#" + url.getRef()));
|
||||
if (p != null) {
|
||||
uc = newurl.openConnection(p);
|
||||
} else {
|
||||
uc = newurl.openConnection();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
uc = null;
|
||||
}
|
||||
if (uc == null) {
|
||||
throw new IOException("Unable to connect to: " +
|
||||
url.toExternalForm());
|
||||
}
|
||||
return uc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method to be overriden by Java Plug-in. [stanleyh]
|
||||
*/
|
||||
protected URLConnection createFileURLConnection(URL url, File file) {
|
||||
return new FileURLConnection(url, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the host components of two URLs.
|
||||
* @param u1 the URL of the first host to compare
|
||||
* @param u2 the URL of the second host to compare
|
||||
* @return <tt>true</tt> if and only if they
|
||||
* are equal, <tt>false</tt> otherwise.
|
||||
*/
|
||||
protected boolean hostsEqual(URL u1, URL u2) {
|
||||
/*
|
||||
* Special case for file: URLs
|
||||
* per RFC 1738 no hostname is equivalent to 'localhost'
|
||||
* i.e. file:///path is equal to file://localhost/path
|
||||
*/
|
||||
String s1 = u1.getHost();
|
||||
String s2 = u2.getHost();
|
||||
if ("localhost".equalsIgnoreCase(s1) && ( s2 == null || "".equals(s2)))
|
||||
return true;
|
||||
if ("localhost".equalsIgnoreCase(s2) && ( s1 == null || "".equals(s1)))
|
||||
return true;
|
||||
return super.hostsEqual(u1, u2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. 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.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilePermission;
|
||||
import java.net.URL;
|
||||
import java.security.Permission;
|
||||
|
||||
final class UNCFileURLConnection extends FileURLConnection {
|
||||
|
||||
private final String effectivePath;
|
||||
private volatile Permission permission;
|
||||
|
||||
UNCFileURLConnection(URL u, File file, String effectivePath) {
|
||||
super(u, file);
|
||||
this.effectivePath = effectivePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Permission getPermission() {
|
||||
Permission perm = permission;
|
||||
if (perm == null) {
|
||||
permission = perm = new FilePermission(effectivePath, "read");
|
||||
}
|
||||
return perm;
|
||||
}
|
||||
}
|
||||
|
||||
692
jdkSrc/jdk8/sun/net/www/protocol/ftp/FtpURLConnection.java
Normal file
692
jdkSrc/jdk8/sun/net/www/protocol/ftp/FtpURLConnection.java
Normal file
@@ -0,0 +1,692 @@
|
||||
/*
|
||||
* Copyright (c) 1994, 2025, 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.MalformedURLException;
|
||||
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 sun.net.NetworkClient;
|
||||
import sun.net.util.IPAddressUtil;
|
||||
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;
|
||||
|
||||
import static sun.net.util.ProxyUtil.copyProxy;
|
||||
|
||||
/**
|
||||
* 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 :
|
||||
* <P>
|
||||
* <code>URL url = new URL("ftp://ftp.sun.com/pub/test.txt");<p>
|
||||
* UrlConnection con = url.openConnection();<p>
|
||||
* InputStream is = con.getInputStream();<p>
|
||||
* ...<p>
|
||||
* is.close();</code>
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static URL checkURL(URL u) throws IllegalArgumentException {
|
||||
if (u != null) {
|
||||
if (u.toExternalForm().indexOf('\n') > -1) {
|
||||
Exception mfue = new MalformedURLException("Illegal character in URL");
|
||||
throw new IllegalArgumentException(mfue.getMessage(), mfue);
|
||||
}
|
||||
}
|
||||
String s = IPAddressUtil.checkAuthority(u);
|
||||
if (s != null) {
|
||||
Exception mfue = new MalformedURLException(s);
|
||||
throw new IllegalArgumentException(mfue.getMessage(), mfue);
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an FtpURLConnection from a URL.
|
||||
*
|
||||
* @param url The <code>URL</code> 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(checkURL(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 = copyProxy(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";
|
||||
String vers = java.security.AccessController.doPrivileged(
|
||||
new GetPropertyAction("java.version"));
|
||||
password = java.security.AccessController.doPrivileged(
|
||||
new GetPropertyAction("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</code> 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</code> 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</code> associated with the host & port.
|
||||
*
|
||||
* @return The <code>Permission</code> 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</code>").
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
60
jdkSrc/jdk8/sun/net/www/protocol/ftp/Handler.java
Normal file
60
jdkSrc/jdk8/sun/net/www/protocol/ftp/Handler.java
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 1994, 2025, 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 static sun.net.util.ProxyUtil.copyProxy;
|
||||
|
||||
/** 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 proxy)
|
||||
throws IOException {
|
||||
return new FtpURLConnection(u, copyProxy(proxy));
|
||||
}
|
||||
}
|
||||
71
jdkSrc/jdk8/sun/net/www/protocol/http/AuthCache.java
Normal file
71
jdkSrc/jdk8/sun/net/www/protocol/http/AuthCache.java
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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] Between 4 and 6 fields separated by ":"
|
||||
* 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
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
108
jdkSrc/jdk8/sun/net/www/protocol/http/AuthCacheImpl.java
Normal file
108
jdkSrc/jdk8/sun/net/www/protocol/http/AuthCacheImpl.java
Normal 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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
100
jdkSrc/jdk8/sun/net/www/protocol/http/AuthCacheValue.java
Normal file
100
jdkSrc/jdk8/sun/net/www/protocol/http/AuthCacheValue.java
Normal 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()
|
||||
*/
|
||||
static protected 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();
|
||||
}
|
||||
38
jdkSrc/jdk8/sun/net/www/protocol/http/AuthScheme.java
Normal file
38
jdkSrc/jdk8/sun/net/www/protocol/http/AuthScheme.java
Normal 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;
|
||||
}
|
||||
|
||||
279
jdkSrc/jdk8/sun/net/www/protocol/http/AuthenticationHeader.java
Normal file
279
jdkSrc/jdk8/sun/net/www/protocol/http/AuthenticationHeader.java
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* 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 sun.net.www.*;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 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 = java.security.AccessController.doPrivileged(
|
||||
new sun.security.action.GetPropertyAction("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;
|
||||
}
|
||||
}
|
||||
463
jdkSrc/jdk8/sun/net/www/protocol/http/AuthenticationInfo.java
Normal file
463
jdkSrc/jdk8/sun/net/www/protocol/http/AuthenticationInfo.java
Normal file
@@ -0,0 +1,463 @@
|
||||
/*
|
||||
* 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 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: */
|
||||
|
||||
transient protected 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.
|
||||
*/
|
||||
static private 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
|
||||
*/
|
||||
static private 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.
|
||||
*/
|
||||
static private 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;
|
||||
|
||||
/** Use this constructor only for proxy entries */
|
||||
public AuthenticationInfo(char type, AuthScheme authScheme, String host, int port, String realm) {
|
||||
this.type = type;
|
||||
this.authScheme = authScheme;
|
||||
this.protocol = "";
|
||||
this.host = host.toLowerCase();
|
||||
this.port = port;
|
||||
this.realm = realm;
|
||||
this.path = null;
|
||||
}
|
||||
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
int port = url.getPort();
|
||||
if (port == -1) {
|
||||
port = url.getDefaultPort();
|
||||
}
|
||||
String key = SERVER_AUTHENTICATION + ":" + url.getProtocol().toLowerCase()
|
||||
+ ":" + url.getHost().toLowerCase() + ":" + port;
|
||||
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) {
|
||||
int port = url.getPort();
|
||||
if (port == -1) {
|
||||
port = url.getDefaultPort();
|
||||
}
|
||||
String key = SERVER_AUTHENTICATION + ":" + scheme + ":" + url.getProtocol().toLowerCase()
|
||||
+ ":" + url.getHost().toLowerCase() + ":" + port + ":" + realm;
|
||||
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 key = PROXY_AUTHENTICATION + "::" + host.toLowerCase() + ":" + port;
|
||||
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 key = PROXY_AUTHENTICATION + ":" + scheme + "::" + host.toLowerCase()
|
||||
+ ":" + port + ":" + realm;
|
||||
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.
|
||||
if (includeRealm) {
|
||||
return type + ":" + authScheme + ":" + protocol + ":"
|
||||
+ host + ":" + port + ":" + realm;
|
||||
} else {
|
||||
return type + ":" + protocol + ":" + host + ":" + port;
|
||||
}
|
||||
}
|
||||
|
||||
String s1, s2; /* used for serialization of pw */
|
||||
|
||||
private void readObject(ObjectInputStream s)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
s.defaultReadObject ();
|
||||
pw = new PasswordAuthentication (s1, s2.toCharArray());
|
||||
s1 = null; s2= null;
|
||||
}
|
||||
|
||||
private synchronized void writeObject(java.io.ObjectOutputStream s)
|
||||
throws IOException
|
||||
{
|
||||
s1 = pw.getUserName();
|
||||
s2 = new String (pw.getPassword());
|
||||
s.defaultWriteObject ();
|
||||
}
|
||||
}
|
||||
205
jdkSrc/jdk8/sun/net/www/protocol/http/BasicAuthentication.java
Normal file
205
jdkSrc/jdk8/sun/net/www/protocol/http/BasicAuthentication.java
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* 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.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 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) {
|
||||
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
|
||||
AuthScheme.BASIC, host, port, realm);
|
||||
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) {
|
||||
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
|
||||
AuthScheme.BASIC, host, port, realm);
|
||||
this.auth = "Basic " + auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a BasicAuthentication
|
||||
*/
|
||||
public BasicAuthentication(boolean isProxy, URL url, String realm,
|
||||
PasswordAuthentication pw) {
|
||||
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
|
||||
AuthScheme.BASIC, url, realm);
|
||||
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) {
|
||||
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
|
||||
AuthScheme.BASIC, url, realm);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
544
jdkSrc/jdk8/sun/net/www/protocol/http/DigestAuthentication.java
Normal file
544
jdkSrc/jdk8/sun/net/www/protocol/http/DigestAuthentication.java
Normal file
@@ -0,0 +1,544 @@
|
||||
/*
|
||||
* 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.http;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.PasswordAuthentication;
|
||||
import java.util.Arrays;
|
||||
import java.util.StringTokenizer;
|
||||
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 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 final static 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<Boolean>() {
|
||||
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) {
|
||||
StringTokenizer st = new StringTokenizer (qop, " ");
|
||||
while (st.hasMoreTokens()) {
|
||||
if (st.nextToken().equalsIgnoreCase ("auth")) {
|
||||
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 (!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) {
|
||||
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
|
||||
AuthScheme.DIGEST,
|
||||
url,
|
||||
realm);
|
||||
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) {
|
||||
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
|
||||
AuthScheme.DIGEST,
|
||||
host,
|
||||
port,
|
||||
realm);
|
||||
this.authMethod = authMethod;
|
||||
this.pw = pw;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this authentication supports preemptive authorization
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsPreemptiveAuthorization() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reclaculates 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reclaculates 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 final static char charArray[] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
|
||||
};
|
||||
|
||||
private final static 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();
|
||||
|
||||
StringBuffer res = new StringBuffer(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();
|
||||
}
|
||||
}
|
||||
64
jdkSrc/jdk8/sun/net/www/protocol/http/Handler.java
Normal file
64
jdkSrc/jdk8/sun/net/www/protocol/http/Handler.java
Normal 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);
|
||||
}
|
||||
}
|
||||
76
jdkSrc/jdk8/sun/net/www/protocol/http/HttpAuthenticator.java
Normal file
76
jdkSrc/jdk8/sun/net/www/protocol/http/HttpAuthenticator.java
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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:
|
||||
* --> 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>
|
||||
*/
|
||||
|
||||
public String authString (URL u, String scheme, String realm);
|
||||
|
||||
}
|
||||
108
jdkSrc/jdk8/sun/net/www/protocol/http/HttpCallerInfo.java
Normal file
108
jdkSrc/jdk8/sun/net/www/protocol/http/HttpCallerInfo.java
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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.
|
||||
*/
|
||||
final public class HttpCallerInfo {
|
||||
// All info that an Authenticator needs.
|
||||
final public URL url;
|
||||
final public String host, protocol, prompt, scheme;
|
||||
final public int port;
|
||||
final public InetAddress addr;
|
||||
final public RequestorType authType;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor an un-schemed object for site access.
|
||||
*/
|
||||
public HttpCallerInfo(URL url) {
|
||||
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 = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor an un-schemed object for proxy access.
|
||||
*/
|
||||
public HttpCallerInfo(URL url, String host, int port) {
|
||||
this.url= url;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
prompt = "";
|
||||
addr = null;
|
||||
protocol = url.getProtocol();
|
||||
authType = RequestorType.PROXY;
|
||||
scheme = "";
|
||||
}
|
||||
}
|
||||
3834
jdkSrc/jdk8/sun/net/www/protocol/http/HttpURLConnection.java
Normal file
3834
jdkSrc/jdk8/sun/net/www/protocol/http/HttpURLConnection.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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.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> threeArgCtr;
|
||||
private final Constructor<? extends AuthenticationInfo> fiveArgCtr;
|
||||
|
||||
private NTLMAuthenticationProxy(Constructor<? extends AuthenticationInfo> threeArgCtr,
|
||||
Constructor<? extends AuthenticationInfo> fiveArgCtr) {
|
||||
this.threeArgCtr = threeArgCtr;
|
||||
this.fiveArgCtr = fiveArgCtr;
|
||||
}
|
||||
|
||||
|
||||
AuthenticationInfo create(boolean isProxy,
|
||||
URL url,
|
||||
PasswordAuthentication pw) {
|
||||
try {
|
||||
return threeArgCtr.newInstance(isProxy, url, pw);
|
||||
} catch (ReflectiveOperationException roe) {
|
||||
finest(roe);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
AuthenticationInfo create(boolean isProxy,
|
||||
String host,
|
||||
int port,
|
||||
PasswordAuthentication pw) {
|
||||
try {
|
||||
return fiveArgCtr.newInstance(isProxy, host, port, pw);
|
||||
} 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> threeArg, fiveArg;
|
||||
try {
|
||||
cl = (Class<? extends AuthenticationInfo>)Class.forName(clazzStr, true, null);
|
||||
if (cl != null) {
|
||||
threeArg = cl.getConstructor(boolean.class,
|
||||
URL.class,
|
||||
PasswordAuthentication.class);
|
||||
fiveArg = cl.getConstructor(boolean.class,
|
||||
String.class,
|
||||
int.class,
|
||||
PasswordAuthentication.class);
|
||||
supportsTA = cl.getDeclaredMethod(supportsTAStr);
|
||||
isTrustedSite = cl.getDeclaredMethod(isTrustedSiteStr, java.net.URL.class);
|
||||
return new NTLMAuthenticationProxy(threeArg,
|
||||
fiveArg);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 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.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 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;
|
||||
|
||||
final private 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 = java.security.AccessController.doPrivileged(
|
||||
new sun.security.action.GetPropertyAction("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,
|
||||
"");
|
||||
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. 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
|
||||
*/
|
||||
synchronized public static boolean isSupported(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<HashMap<String, Negotiator>>() {
|
||||
@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.
|
||||
* @returns 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>
|
||||
* @returns 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.
|
||||
|
||||
}
|
||||
84
jdkSrc/jdk8/sun/net/www/protocol/http/Negotiator.java
Normal file
84
jdkSrc/jdk8/sun/net/www/protocol/http/Negotiator.java
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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, null);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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.logging;
|
||||
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.regex.*;
|
||||
|
||||
/**
|
||||
* A Formatter to make the HTTP logs a bit more palatable to the developer
|
||||
* looking at them. The idea is to present the HTTP events in such a way that
|
||||
* commands and headers are easily spotted (i.e. on separate lines).
|
||||
* @author jccollet
|
||||
*/
|
||||
public class HttpLogFormatter extends java.util.logging.SimpleFormatter {
|
||||
// Pattern for MessageHeader data. Mostly pairs within curly brackets
|
||||
private static volatile Pattern pattern = null;
|
||||
// Pattern for Cookies
|
||||
private static volatile Pattern cpattern = null;
|
||||
|
||||
public HttpLogFormatter() {
|
||||
if (pattern == null) {
|
||||
pattern = Pattern.compile("\\{[^\\}]*\\}");
|
||||
cpattern = Pattern.compile("[^,\\] ]{2,}");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(LogRecord record) {
|
||||
String sourceClassName = record.getSourceClassName();
|
||||
if (sourceClassName == null ||
|
||||
!(sourceClassName.startsWith("sun.net.www.protocol.http") ||
|
||||
sourceClassName.startsWith("sun.net.www.http"))) {
|
||||
return super.format(record);
|
||||
}
|
||||
String src = record.getMessage();
|
||||
StringBuilder buf = new StringBuilder("HTTP: ");
|
||||
if (src.startsWith("sun.net.www.MessageHeader@")) {
|
||||
// MessageHeader logs are composed of pairs within curly brackets
|
||||
// Let's extract them to make it more readable. That way we get one
|
||||
// header pair (name, value) per line. A lot easier to read.
|
||||
Matcher match = pattern.matcher(src);
|
||||
while (match.find()) {
|
||||
int i = match.start();
|
||||
int j = match.end();
|
||||
String s = src.substring(i + 1, j - 1);
|
||||
if (s.startsWith("null: ")) {
|
||||
s = s.substring(6);
|
||||
}
|
||||
if (s.endsWith(": null")) {
|
||||
s = s.substring(0, s.length() - 6);
|
||||
}
|
||||
buf.append("\t").append(s).append("\n");
|
||||
}
|
||||
} else if (src.startsWith("Cookies retrieved: {")) {
|
||||
// This comes from the Cookie handler, let's clean up the format a bit
|
||||
String s = src.substring(20);
|
||||
buf.append("Cookies from handler:\n");
|
||||
while (s.length() >= 7) {
|
||||
if (s.startsWith("Cookie=[")) {
|
||||
String s2 = s.substring(8);
|
||||
int c = s2.indexOf("Cookie2=[");
|
||||
if (c > 0) {
|
||||
s2 = s2.substring(0, c-1);
|
||||
s = s2.substring(c);
|
||||
} else {
|
||||
s = "";
|
||||
}
|
||||
if (s2.length() < 4) {
|
||||
continue;
|
||||
}
|
||||
Matcher m = cpattern.matcher(s2);
|
||||
while (m.find()) {
|
||||
int i = m.start();
|
||||
int j = m.end();
|
||||
if (i >= 0) {
|
||||
String cookie = s2.substring(i + 1, j > 0 ? j - 1 : s2.length() - 1);
|
||||
buf.append("\t").append(cookie).append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s.startsWith("Cookie2=[")) {
|
||||
String s2 = s.substring(9);
|
||||
int c = s2.indexOf("Cookie=[");
|
||||
if (c > 0) {
|
||||
s2 = s2.substring(0, c-1);
|
||||
s = s2.substring(c);
|
||||
} else {
|
||||
s = "";
|
||||
}
|
||||
Matcher m = cpattern.matcher(s2);
|
||||
while (m.find()) {
|
||||
int i = m.start();
|
||||
int j = m.end();
|
||||
if (i >= 0) {
|
||||
String cookie = s2.substring(i+1, j > 0 ? j-1 : s2.length() - 1);
|
||||
buf.append("\t").append(cookie).append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Anything else we let as is.
|
||||
buf.append(src).append("\n");
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.ntlm;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
|
||||
/*
|
||||
* Hooks into Windows implementation of NTLM.
|
||||
* This class will be replaced if a cross-platform version of NTLM
|
||||
* is implemented in the future.
|
||||
*/
|
||||
|
||||
public class NTLMAuthSequence {
|
||||
|
||||
private String username;
|
||||
private String password;
|
||||
private String ntdomain;
|
||||
private int state;
|
||||
private long crdHandle;
|
||||
private long ctxHandle;
|
||||
|
||||
static {
|
||||
initFirst(Status.class);
|
||||
}
|
||||
|
||||
// Used by native code to indicate when a particular protocol sequence is completed
|
||||
// and must not be re-used.
|
||||
|
||||
class Status {
|
||||
boolean sequenceComplete;
|
||||
}
|
||||
|
||||
Status status;
|
||||
|
||||
NTLMAuthSequence (String username, String password, String ntdomain)
|
||||
throws IOException
|
||||
{
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.ntdomain = ntdomain;
|
||||
this.status = new Status();
|
||||
state = 0;
|
||||
crdHandle = getCredentialsHandle (username, ntdomain, password);
|
||||
if (crdHandle == 0) {
|
||||
throw new IOException ("could not get credentials handle");
|
||||
}
|
||||
}
|
||||
|
||||
public String getAuthHeader (String token) throws IOException {
|
||||
byte[] input = null;
|
||||
|
||||
assert !status.sequenceComplete;
|
||||
|
||||
if (token != null)
|
||||
input = Base64.getDecoder().decode(token);
|
||||
byte[] b = getNextToken (crdHandle, input, status);
|
||||
if (b == null)
|
||||
throw new IOException ("Internal authentication error");
|
||||
return Base64.getEncoder().encodeToString(b);
|
||||
}
|
||||
|
||||
public boolean isComplete() {
|
||||
return status.sequenceComplete;
|
||||
}
|
||||
|
||||
private native static void initFirst (Class<NTLMAuthSequence.Status> clazz);
|
||||
|
||||
private native long getCredentialsHandle (String user, String domain, String password);
|
||||
|
||||
private native byte[] getNextToken (long crdHandle, byte[] lastToken, Status returned);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. 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.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.PasswordAuthentication;
|
||||
import java.net.UnknownHostException;
|
||||
import java.net.URL;
|
||||
import sun.net.NetProperties;
|
||||
import sun.net.www.HeaderParser;
|
||||
import sun.net.www.protocol.http.AuthenticationInfo;
|
||||
import sun.net.www.protocol.http.AuthScheme;
|
||||
import sun.net.www.protocol.http.HttpURLConnection;
|
||||
|
||||
/**
|
||||
* NTLMAuthentication:
|
||||
*
|
||||
* @author Michael McMahon
|
||||
*/
|
||||
|
||||
public class NTLMAuthentication extends AuthenticationInfo {
|
||||
|
||||
private static final long serialVersionUID = 100L;
|
||||
|
||||
private static final NTLMAuthenticationCallback NTLMAuthCallback =
|
||||
NTLMAuthenticationCallback.getNTLMAuthenticationCallback();
|
||||
|
||||
private String hostname;
|
||||
private static String defaultDomain; /* Domain to use if not specified by user */
|
||||
private static final boolean ntlmCache; /* Whether cache is enabled for NTLM */
|
||||
|
||||
enum TransparentAuth {
|
||||
DISABLED, // disable for all hosts (default)
|
||||
TRUSTED_HOSTS, // use Windows trusted hosts settings
|
||||
ALL_HOSTS // attempt for all hosts
|
||||
}
|
||||
|
||||
private static final TransparentAuth authMode;
|
||||
|
||||
static {
|
||||
defaultDomain = java.security.AccessController.doPrivileged(
|
||||
new sun.security.action.GetPropertyAction("http.auth.ntlm.domain",
|
||||
"domain"));
|
||||
String ntlmCacheProp = java.security.AccessController.doPrivileged(
|
||||
new sun.security.action.GetPropertyAction("jdk.ntlm.cache", "true"));
|
||||
ntlmCache = Boolean.parseBoolean(ntlmCacheProp);
|
||||
String modeProp = java.security.AccessController.doPrivileged(
|
||||
new java.security.PrivilegedAction<String>() {
|
||||
public String run() {
|
||||
return NetProperties.get("jdk.http.ntlm.transparentAuth");
|
||||
}
|
||||
});
|
||||
|
||||
if ("trustedHosts".equalsIgnoreCase(modeProp))
|
||||
authMode = TransparentAuth.TRUSTED_HOSTS;
|
||||
else if ("allHosts".equalsIgnoreCase(modeProp))
|
||||
authMode = TransparentAuth.ALL_HOSTS;
|
||||
else
|
||||
authMode = TransparentAuth.DISABLED;
|
||||
};
|
||||
|
||||
private void init0() {
|
||||
|
||||
hostname = java.security.AccessController.doPrivileged(
|
||||
new java.security.PrivilegedAction<String>() {
|
||||
public String run() {
|
||||
String localhost;
|
||||
try {
|
||||
localhost = InetAddress.getLocalHost().getHostName().toUpperCase();
|
||||
} catch (UnknownHostException e) {
|
||||
localhost = "localhost";
|
||||
}
|
||||
return localhost;
|
||||
}
|
||||
});
|
||||
int x = hostname.indexOf ('.');
|
||||
if (x != -1) {
|
||||
hostname = hostname.substring (0, x);
|
||||
}
|
||||
}
|
||||
|
||||
String username;
|
||||
String ntdomain;
|
||||
String password;
|
||||
|
||||
/**
|
||||
* Create a NTLMAuthentication:
|
||||
* Username may be specified as domain<BACKSLASH>username in the application Authenticator.
|
||||
* If this notation is not used, then the domain will be taken
|
||||
* from a system property: "http.auth.ntlm.domain".
|
||||
*/
|
||||
public NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw) {
|
||||
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
|
||||
AuthScheme.NTLM,
|
||||
url,
|
||||
"");
|
||||
init (pw);
|
||||
}
|
||||
|
||||
private void init (PasswordAuthentication pw) {
|
||||
this.pw = pw;
|
||||
if (pw != null) {
|
||||
String s = pw.getUserName();
|
||||
int i = s.indexOf ('\\');
|
||||
if (i == -1) {
|
||||
username = s;
|
||||
ntdomain = defaultDomain;
|
||||
} else {
|
||||
ntdomain = s.substring (0, i).toUpperCase();
|
||||
username = s.substring (i+1);
|
||||
}
|
||||
password = new String (pw.getPassword());
|
||||
} else {
|
||||
/* credentials will be acquired from OS */
|
||||
username = null;
|
||||
ntdomain = null;
|
||||
password = null;
|
||||
}
|
||||
init0();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor used for proxy entries
|
||||
*/
|
||||
public NTLMAuthentication(boolean isProxy, String host, int port,
|
||||
PasswordAuthentication pw) {
|
||||
super(isProxy?PROXY_AUTHENTICATION:SERVER_AUTHENTICATION,
|
||||
AuthScheme.NTLM,
|
||||
host,
|
||||
port,
|
||||
"");
|
||||
init (pw);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean useAuthCache() {
|
||||
return ntlmCache && super.useAuthCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this authentication supports preemptive authorization
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsPreemptiveAuthorization() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if NTLM supported transparently (no password needed, SSO)
|
||||
*/
|
||||
public static boolean supportsTransparentAuth() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given site is trusted, i.e. we can try
|
||||
* transparent Authentication.
|
||||
*/
|
||||
public static boolean isTrustedSite(URL url) {
|
||||
if (NTLMAuthCallback != null)
|
||||
return NTLMAuthCallback.isTrustedSite(url);
|
||||
|
||||
switch (authMode) {
|
||||
case TRUSTED_HOSTS:
|
||||
return isTrustedSite(url.toString());
|
||||
case ALL_HOSTS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final boolean isTrustedSiteAvailable = isTrustedSiteAvailable();
|
||||
|
||||
private static native boolean isTrustedSiteAvailable();
|
||||
|
||||
private static boolean isTrustedSite(String url) {
|
||||
if (isTrustedSiteAvailable)
|
||||
return isTrustedSite0(url);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static native boolean isTrustedSite0(String url);
|
||||
|
||||
/**
|
||||
* 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 ntlm */
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
NTLMAuthSequence seq = (NTLMAuthSequence)conn.authObj();
|
||||
if (seq == null) {
|
||||
seq = new NTLMAuthSequence (username, password, ntdomain);
|
||||
conn.authObj(seq);
|
||||
}
|
||||
String response = "NTLM " + seq.getAuthHeader (raw.length()>6?raw.substring(5):null);
|
||||
conn.setAuthenticationProperty(getHeaderName(), response);
|
||||
if (seq.isComplete()) {
|
||||
conn.authObj(null);
|
||||
}
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
conn.authObj(null);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 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.http.spnego;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Authenticator;
|
||||
import java.net.PasswordAuthentication;
|
||||
import java.util.Arrays;
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.callback.NameCallback;
|
||||
import javax.security.auth.callback.PasswordCallback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
import sun.net.www.protocol.http.HttpCallerInfo;
|
||||
import sun.security.jgss.LoginConfigImpl;
|
||||
|
||||
/**
|
||||
* @since 1.6
|
||||
* Special callback handler used in JGSS for the HttpCaller.
|
||||
*/
|
||||
public class NegotiateCallbackHandler implements CallbackHandler {
|
||||
|
||||
private String username;
|
||||
private char[] password;
|
||||
|
||||
/**
|
||||
* Authenticator asks for username and password in a single prompt,
|
||||
* but CallbackHandler checks one by one. So, no matter which callback
|
||||
* gets handled first, make sure Authenticator is only called once.
|
||||
*/
|
||||
private boolean answered;
|
||||
|
||||
private final HttpCallerInfo hci;
|
||||
|
||||
public NegotiateCallbackHandler(HttpCallerInfo hci) {
|
||||
this.hci = hci;
|
||||
}
|
||||
|
||||
private void getAnswer() {
|
||||
if (!answered) {
|
||||
answered = true;
|
||||
|
||||
if (LoginConfigImpl.HTTP_USE_GLOBAL_CREDS) {
|
||||
PasswordAuthentication passAuth =
|
||||
Authenticator.requestPasswordAuthentication(
|
||||
hci.host, hci.addr, hci.port, hci.protocol,
|
||||
hci.prompt, hci.scheme, hci.url, hci.authType);
|
||||
/**
|
||||
* To be compatible with existing callback handler implementations,
|
||||
* when the underlying Authenticator is canceled, username and
|
||||
* password are assigned null. No exception is thrown.
|
||||
*/
|
||||
if (passAuth != null) {
|
||||
username = passAuth.getUserName();
|
||||
password = passAuth.getPassword();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handle(Callback[] callbacks) throws
|
||||
UnsupportedCallbackException, IOException {
|
||||
for (int i=0; i<callbacks.length; i++) {
|
||||
Callback callBack = callbacks[i];
|
||||
|
||||
if (callBack instanceof NameCallback) {
|
||||
getAnswer();
|
||||
((NameCallback)callBack).setName(username);
|
||||
} else if (callBack instanceof PasswordCallback) {
|
||||
getAnswer();
|
||||
((PasswordCallback)callBack).setPassword(password);
|
||||
if (password != null) Arrays.fill(password, ' ');
|
||||
} else {
|
||||
throw new UnsupportedCallbackException(callBack,
|
||||
"Call back not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
159
jdkSrc/jdk8/sun/net/www/protocol/http/spnego/NegotiatorImpl.java
Normal file
159
jdkSrc/jdk8/sun/net/www/protocol/http/spnego/NegotiatorImpl.java
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 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.spnego;
|
||||
|
||||
import com.sun.security.jgss.ExtendedGSSContext;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.ietf.jgss.GSSContext;
|
||||
import org.ietf.jgss.GSSException;
|
||||
import org.ietf.jgss.GSSName;
|
||||
import org.ietf.jgss.Oid;
|
||||
|
||||
import sun.net.www.protocol.http.HttpCallerInfo;
|
||||
import sun.net.www.protocol.http.Negotiator;
|
||||
import sun.security.jgss.GSSManagerImpl;
|
||||
import sun.security.jgss.GSSUtil;
|
||||
import sun.security.jgss.HttpCaller;
|
||||
|
||||
/**
|
||||
* This class encapsulates all JAAS and JGSS API calls in a separate class
|
||||
* outside NegotiateAuthentication.java so that J2SE build can go smoothly
|
||||
* without the presence of it.
|
||||
*
|
||||
* @author weijun.wang@sun.com
|
||||
* @since 1.6
|
||||
*/
|
||||
public class NegotiatorImpl extends Negotiator {
|
||||
|
||||
private static final boolean DEBUG =
|
||||
java.security.AccessController.doPrivileged(
|
||||
new sun.security.action.GetBooleanAction("sun.security.krb5.debug"));
|
||||
|
||||
private GSSContext context;
|
||||
private byte[] oneToken;
|
||||
|
||||
/**
|
||||
* Initialize the object, which includes:<ul>
|
||||
* <li>Find out what GSS mechanism to use from the system property
|
||||
* <code>http.negotiate.mechanism.oid</code>, defaults SPNEGO
|
||||
* <li>Creating the GSSName for the target host, "HTTP/"+hostname
|
||||
* <li>Creating GSSContext
|
||||
* <li>A first call to initSecContext</ul>
|
||||
*/
|
||||
private void init(HttpCallerInfo hci) throws GSSException {
|
||||
final Oid oid;
|
||||
|
||||
if (hci.scheme.equalsIgnoreCase("Kerberos")) {
|
||||
// we can only use Kerberos mech when the scheme is kerberos
|
||||
oid = GSSUtil.GSS_KRB5_MECH_OID;
|
||||
} else {
|
||||
String pref = java.security.AccessController.doPrivileged(
|
||||
new java.security.PrivilegedAction<String>() {
|
||||
public String run() {
|
||||
return System.getProperty(
|
||||
"http.auth.preference",
|
||||
"spnego");
|
||||
}
|
||||
});
|
||||
if (pref.equalsIgnoreCase("kerberos")) {
|
||||
oid = GSSUtil.GSS_KRB5_MECH_OID;
|
||||
} else {
|
||||
// currently there is no 3rd mech we can use
|
||||
oid = GSSUtil.GSS_SPNEGO_MECH_OID;
|
||||
}
|
||||
}
|
||||
|
||||
GSSManagerImpl manager = new GSSManagerImpl(
|
||||
new HttpCaller(hci));
|
||||
|
||||
// RFC 4559 4.1 uses uppercase service name "HTTP".
|
||||
// RFC 4120 6.2.1 demands the host be lowercase
|
||||
String peerName = "HTTP@" + hci.host.toLowerCase();
|
||||
|
||||
GSSName serverName = manager.createName(peerName,
|
||||
GSSName.NT_HOSTBASED_SERVICE);
|
||||
context = manager.createContext(serverName,
|
||||
oid,
|
||||
null,
|
||||
GSSContext.DEFAULT_LIFETIME);
|
||||
|
||||
// Always respect delegation policy in HTTP/SPNEGO.
|
||||
if (context instanceof ExtendedGSSContext) {
|
||||
((ExtendedGSSContext)context).requestDelegPolicy(true);
|
||||
}
|
||||
oneToken = context.initSecContext(new byte[0], 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @throws java.io.IOException If negotiator cannot be constructed
|
||||
*/
|
||||
public NegotiatorImpl(HttpCallerInfo hci) throws IOException {
|
||||
try {
|
||||
init(hci);
|
||||
} catch (GSSException e) {
|
||||
if (DEBUG) {
|
||||
System.out.println("Negotiate support not initiated, will " +
|
||||
"fallback to other scheme if allowed. Reason:");
|
||||
e.printStackTrace();
|
||||
}
|
||||
IOException ioe = new IOException("Negotiate support not initiated");
|
||||
ioe.initCause(e);
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first token of GSS, in SPNEGO, it's called NegTokenInit
|
||||
* @return the first token
|
||||
*/
|
||||
@Override
|
||||
public byte[] firstToken() {
|
||||
return oneToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the rest tokens of GSS, in SPNEGO, it's called NegTokenTarg
|
||||
* @param token the token received from server
|
||||
* @return the next token
|
||||
* @throws java.io.IOException if the token cannot be created successfully
|
||||
*/
|
||||
@Override
|
||||
public byte[] nextToken(byte[] token) throws IOException {
|
||||
try {
|
||||
return context.initSecContext(token, 0, token.length);
|
||||
} catch (GSSException e) {
|
||||
if (DEBUG) {
|
||||
System.out.println("Negotiate support cannot continue. Reason:");
|
||||
e.printStackTrace();
|
||||
}
|
||||
IOException ioe = new IOException("Negotiate support cannot continue");
|
||||
ioe.initCause(e);
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2020, 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 {
|
||||
int readTimeout = getReadTimeout();
|
||||
http = HttpsClient.New (getSSLSocketFactory(),
|
||||
url,
|
||||
getHostnameVerifier(),
|
||||
null,
|
||||
-1,
|
||||
useCache,
|
||||
getConnectTimeout(),
|
||||
this);
|
||||
http.setReadTimeout(readTimeout);
|
||||
((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;
|
||||
int readTimeout = getReadTimeout();
|
||||
http = HttpsClient.New (getSSLSocketFactory(),
|
||||
url,
|
||||
getHostnameVerifier(),
|
||||
proxyHost,
|
||||
proxyPort,
|
||||
useCache,
|
||||
getConnectTimeout(),
|
||||
this);
|
||||
http.setReadTimeout(readTimeout);
|
||||
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 X.509 certificate chain, or null if
|
||||
* the server did not authenticate.
|
||||
*/
|
||||
public javax.security.cert.X509Certificate[] getServerCertificateChain()
|
||||
throws SSLPeerUnverifiedException {
|
||||
if (cachedResponse != null) {
|
||||
throw new UnsupportedOperationException("this method is not supported when using cache");
|
||||
}
|
||||
if (http == null) {
|
||||
throw new IllegalStateException("connection not yet open");
|
||||
} else {
|
||||
return ((HttpsClient)http).getServerCertificateChain ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 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 javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
/**
|
||||
* <code>HostnameVerifier</code> provides a callback mechanism so that
|
||||
* implementers of this interface can supply a policy for
|
||||
* handling the case where the host to connect to and
|
||||
* the server name from the certificate mismatch.
|
||||
*
|
||||
* The default implementation will deny such connections.
|
||||
*
|
||||
* @author Xuelei Fan
|
||||
*/
|
||||
final public class DefaultHostnameVerifier implements HostnameVerifier {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2005, 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.
|
||||
*/
|
||||
protected void dispose() throws Throwable {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
64
jdkSrc/jdk8/sun/net/www/protocol/https/Handler.java
Normal file
64
jdkSrc/jdk8/sun/net/www/protocol/https/Handler.java
Normal 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);
|
||||
}
|
||||
}
|
||||
796
jdkSrc/jdk8/sun/net/www/protocol/https/HttpsClient.java
Normal file
796
jdkSrc/jdk8/sun/net/www/protocol/https/HttpsClient.java
Normal file
@@ -0,0 +1,796 @@
|
||||
/*
|
||||
* 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.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.StringTokenizer;
|
||||
import java.util.Vector;
|
||||
import java.security.AccessController;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
|
||||
import javax.net.ssl.*;
|
||||
import sun.net.www.http.HttpClient;
|
||||
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 = AccessController.doPrivileged(
|
||||
new GetPropertyAction("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 = AccessController.doPrivileged(
|
||||
new GetPropertyAction("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 = java.security.AccessController.doPrivileged(
|
||||
new sun.security.action.GetPropertyAction("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() &&
|
||||
"POST".equals(httpuc.getRequestMethod())) {
|
||||
if (!ret.available())
|
||||
ret = null;
|
||||
}
|
||||
|
||||
if (ret != null) {
|
||||
if ((ret.proxy != null && ret.proxy.equals(p)) ||
|
||||
(ret.proxy == null && p == Proxy.NO_PROXY)) {
|
||||
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);
|
||||
} 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 void closeServer() {
|
||||
try {
|
||||
// SSLSocket.close may block up to timeout. Make sure it's short.
|
||||
serverSocket.setSoTimeout(1);
|
||||
} catch (Exception e) {}
|
||||
super.closeServer();
|
||||
}
|
||||
|
||||
@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 X.509 certificate chain with which the server
|
||||
* authenticated itself, or null if the server did not authenticate.
|
||||
*/
|
||||
javax.security.cert.X509Certificate [] getServerCertificateChain()
|
||||
throws SSLPeerUnverifiedException
|
||||
{
|
||||
return session.getPeerCertificateChain();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,552 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.net.MalformedURLException;
|
||||
import java.io.*;
|
||||
import javax.net.ssl.*;
|
||||
import java.security.Permission;
|
||||
import java.security.Principal;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import sun.net.util.IPAddressUtil;
|
||||
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);
|
||||
}
|
||||
|
||||
static URL checkURL(URL u) throws IOException {
|
||||
if (u != null) {
|
||||
if (u.toExternalForm().indexOf('\n') > -1) {
|
||||
throw new MalformedURLException("Illegal character in URL");
|
||||
}
|
||||
}
|
||||
String s = IPAddressUtil.checkAuthority(u);
|
||||
if (s != null) {
|
||||
throw new MalformedURLException(s);
|
||||
}
|
||||
return u;
|
||||
}
|
||||
// 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(checkURL(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 server's X.509 certificate chain, or null if
|
||||
* the server did not authenticate.
|
||||
*
|
||||
* NOTE: This method is not necessary for the version of this class
|
||||
* implementing javax.net.ssl.HttpsURLConnection, but provided for
|
||||
* compatibility with the com.sun.net.ssl.HttpsURLConnection version.
|
||||
*/
|
||||
public javax.security.cert.X509Certificate[] getServerCertificateChain() {
|
||||
try {
|
||||
return delegate.getServerCertificateChain();
|
||||
} catch (SSLPeerUnverifiedException e) {
|
||||
// this method does not throw an exception as declared in
|
||||
// com.sun.net.ssl.HttpsURLConnection.
|
||||
// Return null for compatibility.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 #getRequestProperty(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.
|
||||
*/
|
||||
protected void finalize() throws Throwable {
|
||||
delegate.dispose();
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
return delegate.equals(obj);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
222
jdkSrc/jdk8/sun/net/www/protocol/jar/Handler.java
Normal file
222
jdkSrc/jdk8/sun/net/www/protocol/jar/Handler.java
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. 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;
|
||||
}
|
||||
|
||||
public String checkNestedProtocol(String spec) {
|
||||
if (spec.regionMatches(true, 0, "jar:", 0, 4)) {
|
||||
return "Nested JAR URLs are not supported";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
String exceptionMessage = checkNestedProtocol(spec);
|
||||
if (exceptionMessage != null) {
|
||||
// NPE will be transformed into MalformedURLException by the caller
|
||||
throw new NullPointerException(exceptionMessage);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
173
jdkSrc/jdk8/sun/net/www/protocol/jar/JarFileFactory.java
Normal file
173
jdkSrc/jdk8/sun/net/www/protocol/jar/JarFileFactory.java
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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.protocol.jar;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.HashMap;
|
||||
import java.util.jar.JarFile;
|
||||
import java.security.Permission;
|
||||
import sun.net.util.URLUtil;
|
||||
|
||||
/* A factory for cached JAR file. This class is used to both retrieve
|
||||
* and cache Jar files.
|
||||
*
|
||||
* @author Benjamin Renaud
|
||||
* @since JDK1.2
|
||||
*/
|
||||
class JarFileFactory implements URLJarFile.URLJarFileCloseController {
|
||||
|
||||
/* the url to file cache */
|
||||
private static final HashMap<String, JarFile> fileCache = new HashMap<>();
|
||||
|
||||
/* the file to url cache */
|
||||
private static final HashMap<JarFile, URL> urlCache = new HashMap<>();
|
||||
|
||||
private static final JarFileFactory instance = new JarFileFactory();
|
||||
|
||||
private JarFileFactory() { }
|
||||
|
||||
public static JarFileFactory getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
URLConnection getConnection(JarFile jarFile) throws IOException {
|
||||
URL u;
|
||||
synchronized (instance) {
|
||||
u = urlCache.get(jarFile);
|
||||
}
|
||||
if (u != null)
|
||||
return u.openConnection();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public JarFile get(URL url) throws IOException {
|
||||
return get(url, true);
|
||||
}
|
||||
|
||||
JarFile get(URL url, boolean useCaches) throws IOException {
|
||||
if (url.getProtocol().equalsIgnoreCase("file")) {
|
||||
// Deal with UNC pathnames specially. See 4180841
|
||||
|
||||
String host = url.getHost();
|
||||
if (host != null && !host.equals("") &&
|
||||
!host.equalsIgnoreCase("localhost")) {
|
||||
|
||||
url = new URL("file", "", "//" + host + url.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
JarFile result;
|
||||
JarFile local_result;
|
||||
|
||||
if (useCaches) {
|
||||
synchronized (instance) {
|
||||
result = getCachedJarFile(url);
|
||||
}
|
||||
if (result == null) {
|
||||
local_result = URLJarFile.getJarFile(url, this);
|
||||
synchronized (instance) {
|
||||
result = getCachedJarFile(url);
|
||||
if (result == null) {
|
||||
fileCache.put(URLUtil.urlNoFragString(url), local_result);
|
||||
urlCache.put(local_result, url);
|
||||
result = local_result;
|
||||
} else {
|
||||
if (local_result != null) {
|
||||
local_result.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = URLJarFile.getJarFile(url, this);
|
||||
}
|
||||
if (result == null)
|
||||
throw new FileNotFoundException(url.toString());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method of the URLJarFileCloseController to
|
||||
* indicate that the JarFile is close. This way we can
|
||||
* remove the JarFile from the cache
|
||||
*/
|
||||
public void close(JarFile jarFile) {
|
||||
synchronized (instance) {
|
||||
URL urlRemoved = urlCache.remove(jarFile);
|
||||
if (urlRemoved != null)
|
||||
fileCache.remove(URLUtil.urlNoFragString(urlRemoved));
|
||||
}
|
||||
}
|
||||
|
||||
private JarFile getCachedJarFile(URL url) {
|
||||
assert Thread.holdsLock(instance);
|
||||
JarFile result = fileCache.get(URLUtil.urlNoFragString(url));
|
||||
|
||||
/* if the JAR file is cached, the permission will always be there */
|
||||
if (result != null) {
|
||||
Permission perm = getPermission(result);
|
||||
if (perm != null) {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
try {
|
||||
sm.checkPermission(perm);
|
||||
} catch (SecurityException se) {
|
||||
// fallback to checkRead/checkConnect for pre 1.2
|
||||
// security managers
|
||||
if ((perm instanceof java.io.FilePermission) &&
|
||||
perm.getActions().indexOf("read") != -1) {
|
||||
sm.checkRead(perm.getName());
|
||||
} else if ((perm instanceof
|
||||
java.net.SocketPermission) &&
|
||||
perm.getActions().indexOf("connect") != -1) {
|
||||
sm.checkConnect(url.getHost(), url.getPort());
|
||||
} else {
|
||||
throw se;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Permission getPermission(JarFile jarFile) {
|
||||
try {
|
||||
URLConnection uc = getConnection(jarFile);
|
||||
if (uc != null)
|
||||
return uc.getPermission();
|
||||
} catch (IOException ioe) {
|
||||
// gulp
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
376
jdkSrc/jdk8/sun/net/www/protocol/jar/JarURLConnection.java
Normal file
376
jdkSrc/jdk8/sun/net/www/protocol/jar/JarURLConnection.java
Normal file
@@ -0,0 +1,376 @@
|
||||
/*
|
||||
* 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();
|
||||
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 value 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();
|
||||
}
|
||||
}
|
||||
286
jdkSrc/jdk8/sun/net/www/protocol/jar/URLJarFile.java
Normal file
286
jdkSrc/jdk8/sun/net/www/protocol/jar/URLJarFile.java
Normal file
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 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.*;
|
||||
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))
|
||||
return new URLJarFile(url, closeController);
|
||||
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(URL url, URLJarFileCloseController closeController) throws IOException {
|
||||
super(ParseUtil.decode(url.getFile()));
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* close the jar file.
|
||||
*/
|
||||
protected void finalize() throws IOException {
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) throws IOException {
|
||||
return retrieve(url, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/* get the stream before asserting privileges */
|
||||
try (final InputStream in = url.openConnection().getInputStream()) {
|
||||
result = AccessController.doPrivileged(
|
||||
new PrivilegedExceptionAction<JarFile>() {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
39
jdkSrc/jdk8/sun/net/www/protocol/jar/URLJarFileCallBack.java
Normal file
39
jdkSrc/jdk8/sun/net/www/protocol/jar/URLJarFileCallBack.java
Normal 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;
|
||||
}
|
||||
158
jdkSrc/jdk8/sun/net/www/protocol/mailto/Handler.java
Normal file
158
jdkSrc/jdk8/sun/net/www/protocol/mailto/Handler.java
Normal 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);
|
||||
}
|
||||
}
|
||||
140
jdkSrc/jdk8/sun/net/www/protocol/mailto/MailToURLConnection.java
Normal file
140
jdkSrc/jdk8/sun/net/www/protocol/mailto/MailToURLConnection.java
Normal 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;
|
||||
}
|
||||
}
|
||||
98
jdkSrc/jdk8/sun/net/www/protocol/netdoc/Handler.java
Normal file
98
jdkSrc/jdk8/sun/net/www/protocol/netdoc/Handler.java
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 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.
|
||||
*/
|
||||
|
||||
/*-
|
||||
* netdoc urls point either into the local filesystem or externally
|
||||
* through an http url, with network documents being preferred. Useful for
|
||||
* FAQs & other documents which are likely to be changing over time at the
|
||||
* central site, and where the user will want the most recent edition.
|
||||
*
|
||||
* @author Steven B. Byrne
|
||||
*/
|
||||
|
||||
package sun.net.www.protocol.netdoc;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URLStreamHandler;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class Handler extends URLStreamHandler {
|
||||
static URL base;
|
||||
|
||||
/*
|
||||
* Attempt to find a load the given url using the default (network)
|
||||
* documentation location. If that fails, use the local copy
|
||||
*/
|
||||
public synchronized URLConnection openConnection(URL u)
|
||||
throws IOException
|
||||
{
|
||||
URLConnection uc = null;
|
||||
URL ru;
|
||||
|
||||
Boolean tmp = java.security.AccessController.doPrivileged(
|
||||
new sun.security.action.GetBooleanAction("newdoc.localonly"));
|
||||
boolean localonly = tmp.booleanValue();
|
||||
|
||||
String docurl = java.security.AccessController.doPrivileged(
|
||||
new sun.security.action.GetPropertyAction("doc.url"));
|
||||
|
||||
String file = u.getFile();
|
||||
if (!localonly) {
|
||||
try {
|
||||
if (base == null) {
|
||||
base = new URL(docurl);
|
||||
}
|
||||
ru = new URL(base, file);
|
||||
} catch (MalformedURLException e) {
|
||||
ru = null;
|
||||
}
|
||||
if (ru != null) {
|
||||
uc = ru.openConnection();
|
||||
}
|
||||
}
|
||||
|
||||
if (uc == null) {
|
||||
try {
|
||||
ru = new URL("file", "~", file);
|
||||
|
||||
uc = ru.openConnection();
|
||||
InputStream is = uc.getInputStream(); // Check for success.
|
||||
} catch (MalformedURLException e) {
|
||||
uc = null;
|
||||
} catch (IOException e) {
|
||||
uc = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (uc == null) {
|
||||
throw new IOException("Can't find file for URL: "
|
||||
+u.toExternalForm());
|
||||
}
|
||||
return uc;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user