You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdelibs/tdehtml/java/org/kde/kjas/server/KJASURLStreamHandlerFactory...

610 lines
20 KiB

/* This file is part of the KDE project
*
* Copyright (C) 2003 Koos Vriezen <koos ! vriezen () xs4all ! nl>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
package org.kde.kjas.server;
import java.net.*;
import java.io.*;
import java.util.*;
import java.security.*;
/**
*
*/
class KIOConnection
{
final static int NOT_CONNECTED = 0;
final static int CONNECT_WAIT = 1;
final static int CONNECTED = 2;
final static int DATA = 0;
final static int FINISHED = 1;
final static int ERRORCODE = 2;
final static int CONNECT = 6;
final static int REQUESTDATA = 7;
final static int STOP = 0;
final static int HOLD = 1;
final static int RESUME = 2;
protected static int id = 0;
static Hashtable jobs = new Hashtable(); // should be thread safe
static void setData(String jobid, int code, byte [] data) {
KIOConnection job = (KIOConnection) jobs.get(jobid);
if (job == null || !job.setData(code, data))
Main.info("TDEIO KJASHttpURLConnection gone (timedout/closed)");
else
Thread.yield();
}
private class KJASOutputStream extends OutputStream {
KJASOutputStream() {
}
public void write(int b) throws IOException {
byte[] buf = {(byte)b};
write(buf);
}
public synchronized void write(byte b[], int off, int len) throws IOException {
byte[] buf = new byte[len];
System.arraycopy(b, off, buf, 0, len);
sendData(buf, false);
}
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
public void close() throws IOException {
disconnect();
}
public void flush() throws IOException {
checkConnected();
sendData(null, true);
}
}
private class KJASInputStream extends InputStream {
KJASInputStream() {
}
public int read() throws IOException {
if (getData(true))
return 0x00ff & in_buf[in_bufpos++];
return -1;
}
public int read(byte[] b, int off, int len) throws IOException {
int total = 0;
do {
if (!getData(true)) break;
int nr = in_buf.length - in_bufpos;
if (nr > len)
nr = len;
System.arraycopy(in_buf, in_bufpos, b, off, nr);
len -= nr;
total += nr;
off += nr;
in_bufpos += nr;
} while (len > 0);
return total > 0 ? total : -1;
}
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
public int available() throws IOException {
return inAvailable();
}
public boolean markSupported() {
return false;
}
public void close() throws IOException {
disconnect();
}
}
protected URL url;
protected int connect_status = 0;
protected String jobid = null; // connection id with TDEIO
protected LinkedList data = new LinkedList(); // not thread safe
protected int errorcode = 0;
protected boolean finished = false; // all data has arived
protected boolean onhold = false; // TDEIO job is suspended
protected boolean request_data = false; // need data for put job
private KJASOutputStream out = null;
private KJASInputStream in = null;
private byte [] in_buf = null; // current input buffer
private int in_bufpos = 0; // position in buffer
private boolean in_eof = false; // all data is read
private final static int LOW_BUFFER_LIMIT = 5; // put onhold off
private final static int HIGH_BUFFER_LIMIT = 10; // put onhold on
protected KIOConnection(URL u) {
url = u;
}
protected void checkConnected() throws IOException {
if (connect_status != CONNECTED)
throw new IOException("not connected");
}
protected boolean haveError() {
return errorcode != 0;
}
synchronized protected boolean setData(int code, byte [] d) {
// is job still there when entering the monitor
if (jobs.get(jobid) == null)
return false;
if (connect_status == CONNECT_WAIT)
connect_status = CONNECTED;
switch (code) {
case FINISHED:
if (d != null && d.length > 0)
data.addLast(d);
finished = true;
onhold = false;
jobs.remove(jobid);
Main.debug ("TDEIO FINISHED (" + jobid + ") " + data.size());
break;
case DATA:
if (d.length > 0)
data.addLast(d);
// Main.debug ("TDEIO DATA (" + jobid + ") " + data.size());
if (!onhold && data.size() > HIGH_BUFFER_LIMIT) {
Main.protocol.sendDataCmd(jobid, HOLD);
onhold = true;
}
break;
case ERRORCODE:
String codestr = new String(d);
errorcode = Integer.parseInt(codestr);
Main.debug ("TDEIO ERRORECODE(" + jobid + ") " + errorcode);
break;
case CONNECT:
Main.debug ("TDEIO CONNECT(" + jobid + ") ");
request_data = true;
errorcode = 0;
break;
case REQUESTDATA:
Main.debug ("TDEIO REQUESTDATA(" + jobid + ") ");
request_data = true;
break;
}
notifyAll();
return true;
}
private synchronized boolean getData(boolean mayblock) throws IOException {
if (haveError()) {
//disconnect();
in_eof = true;
//throw new IOException("i/o error " + errorcode);
}
if (in_eof)
return false;
checkConnected();
if (in_buf != null && in_bufpos < in_buf.length)
return true;
int datasize = data.size();
if (datasize > 0) {
in_buf = (byte []) data.removeFirst();
in_bufpos = 0;
}
if (onhold && datasize < LOW_BUFFER_LIMIT) {
Main.protocol.sendDataCmd(jobid, RESUME);
onhold = false;
}
if (datasize > 0)
return true;
if (finished) {
in_eof = true;
return false;
}
if (!mayblock)
return false;
try {
wait();
} catch (InterruptedException ie) {
return false;
}
return getData(false);
}
synchronized private int inAvailable() throws IOException {
if (in_eof)
return 0;
checkConnected();
if (!getData(false))
return 0;
int total = in_buf.length - in_bufpos;
ListIterator it = data.listIterator(0);
while (it.hasNext())
total += ((byte []) it.next()).length;
return total;
}
synchronized private void sendData(byte [] d, boolean force) throws IOException {
Main.debug ("TDEIO sendData(" + jobid + ") force:" + force + " request_data:" + request_data);
if (d != null)
data.addLast(d);
if (!request_data && !force) return;
if (data.size() == 0) return;
if (force && !request_data) {
try {
wait(10000);
} catch (InterruptedException ie) {
return;
}
if (!request_data) {
Main.debug ("TDEIO sendData(" + jobid + ") timeout");
data.clear();
disconnect();
throw new IOException("timeout");
}
}
byte[] buf;
int total = 0;
ListIterator it = data.listIterator(0);
while (it.hasNext())
total += ((byte []) it.next()).length;
buf = new byte[total];
int off = 0;
it = data.listIterator(0);
while (it.hasNext()) {
byte [] b = (byte []) it.next();
System.arraycopy(b, 0, buf, off, b.length);
off += b.length;
}
data.clear();
request_data = false;
Main.protocol.sendPutData(jobid, buf, 0, total);
}
synchronized void connect(boolean doInput) throws IOException {
if (connect_status == CONNECTED)
return; // javadocs: call is ignored
//(new Exception()).printStackTrace();
Main.debug ("TDEIO connect " + url);
errorcode = 0;
finished = in_eof = false;
jobid = String.valueOf(id++);
jobs.put(jobid, this);
if (doInput)
Main.protocol.sendGetURLDataCmd(jobid, url.toExternalForm());
else
Main.protocol.sendPutURLDataCmd(jobid, url.toExternalForm());
connect_status = CONNECT_WAIT;
try {
wait(20000);
} catch (InterruptedException ie) {
errorcode = -1;
}
boolean isconnected = (connect_status == CONNECTED);
if (isconnected && !haveError()) {
if (doInput)
in = new KJASInputStream();
else
out = new KJASOutputStream();
Main.debug ("TDEIO connect(" + jobid + ") " + url);
return;
}
connect_status = NOT_CONNECTED;
jobs.remove(jobid);
if (isconnected) {
if (!finished)
Main.protocol.sendDataCmd(jobid, STOP);
Main.debug ("TDEIO connect error " + url);
throw new ConnectException("connection failed (not found)");
}
Main.debug ("TDEIO connect timeout " + url);
throw new IOException("connection failed (timeout)");
}
synchronized void disconnect() {
if (connect_status == NOT_CONNECTED)
return;
Main.debug ("TDEIO disconnect " + jobid);
//(new Exception()).printStackTrace();
if (out != null) {
try {
out.flush();
} catch (IOException iox) {}
}
connect_status = NOT_CONNECTED;
out = null;
in = null;
if (!finished) {
Main.protocol.sendDataCmd(jobid, STOP);
jobs.remove(jobid);
}
notifyAll();
}
InputStream getInputStream() throws IOException {
Main.debug ("TDEIO getInputStream(" + jobid + ") " + url);
return in;
}
OutputStream getOutputStream() throws IOException {
Main.debug ("TDEIO getOutputStream(" + jobid + ") " + url);
return out;
}
}
final class KIOHttpConnection extends KIOConnection
{
final static int HEADERS = 3;
final static int REDIRECT = 4;
final static int MIMETYPE = 5;
Vector headers = new Vector();
Hashtable headersmap = new Hashtable();
String responseMessage = null;
int responseCode = -1;
KIOHttpConnection(URL u) {
super(u);
}
protected boolean haveError() {
return super.haveError() ||
responseCode != 404 && (responseCode < 0 || responseCode >= 400);
}
protected synchronized boolean setData(int code, byte [] d) {
switch (code) {
case HEADERS:
StringTokenizer tokenizer = new StringTokenizer(new String(d), "\n");
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
int pos = token.indexOf(":");
String [] entry = {
token.substring(0, pos > -1 ? pos : token.length()).toLowerCase(), token.substring(pos > -1 ? pos+1: token.length()).trim()
};
headers.add(entry);
headersmap.put(entry[0], entry[1]);
// Main.debug ("TDEIO header " + entry[0] + "=" + entry[1]);
}
responseCode = 0;
if (headersmap.size() > 0) {
String token = ((String []) headers.get(0))[0];
if (!token.startsWith("http/1.")) break;
int spos = token.indexOf(' ');
if (spos < 0) break;
int epos = token.indexOf(' ', spos + 1);
if (epos < 0) break;
responseCode = Integer.parseInt(token.substring(spos+1, epos));
responseMessage = token.substring(epos+1);
Main.debug ("TDEIO responsecode=" + responseCode);
}
break;
}
return super.setData(code, d);
}
}
final class KIOSimpleConnection extends KIOConnection
{
KIOSimpleConnection(URL u) {
super(u);
}
}
final class KJASHttpURLConnection extends HttpURLConnection
{
private KIOHttpConnection kioconnection;
KJASHttpURLConnection(URL u) {
super(u);
kioconnection = new KIOHttpConnection(u);
}
public Map getHeaderFields() {
try {
connect();
} catch (IOException e) {
Main.debug ("Error on implicit connect()");
}
Main.debug ("TDEIO getHeaderFields");
return kioconnection.headersmap;
}
public String getHeaderField(String name) {
try {
connect();
} catch (IOException e) {
Main.debug ("Error on implicit connect()");
}
String field = (String) kioconnection.headersmap.get(name);
Main.debug ("TDEIO getHeaderField:" + name + "=" + field);
//(new Exception()).printStackTrace();
return field;
}
public String getHeaderField(int n) {
try {
connect();
} catch (IOException e) {
Main.debug ("Error on implicit connect()");
}
Main.debug ("TDEIO getHeaderField(" + n + ") size=" + kioconnection.headersmap.size());
if (n >= kioconnection.headersmap.size())
return null;
String [] entry = (String []) kioconnection.headers.get(n);
String line = entry[0];
if (entry[1].length() > 0)
line += ":" + entry[1];
Main.debug ("TDEIO getHeaderField(" + n + ")=#" + line + "#");
return line;
}
public String getHeaderFieldKey(int n) {
try {
connect();
} catch (IOException e) {
Main.debug ("Error on implicit connect()");
}
Main.debug ("TDEIO getHeaderFieldKey " + n);
if (n >= kioconnection.headersmap.size())
return null;
return ((String []) kioconnection.headers.get(n))[0];
}
public int getResponseCode() throws IOException {
Main.debug ("TDEIO getResponseCode");
if (kioconnection.responseCode == -1) {
try {
connect();
} catch (IOException e) {
if (kioconnection.responseCode == -1)
throw e;
}
}
responseMessage = kioconnection.responseMessage;
return kioconnection.responseCode;
}
public boolean usingProxy() {
return false; // FIXME
}
public void connect() throws IOException {
if (connected)
return;
Main.debug ("TDEIO KJASHttpURLConnection.connect " + url);
SecurityManager security = System.getSecurityManager();
if (security != null)
security.checkPermission(getPermission());
kioconnection.connect(doInput);
connected = true;
if (kioconnection.responseCode == 404)
throw new FileNotFoundException(url.toExternalForm());
}
public void disconnect() {
kioconnection.disconnect();
connected = false;
}
public InputStream getInputStream() throws IOException {
doInput = true;
doOutput = false;
connect();
return kioconnection.getInputStream();
}
public OutputStream getOutputStream() throws IOException {
doInput = false;
doOutput = true;
connect();
return kioconnection.getOutputStream();
}
public InputStream getErrorStream() {
Main.debug("TDEIO KJASHttpURLConnection.getErrorStream" + url);
try {
if (connected && kioconnection.responseCode == 404)
return kioconnection.getInputStream();
} catch (Exception ex) {}
return null;
}
}
final class KJASSimpleURLConnection extends URLConnection
{
private KIOSimpleConnection kioconnection = null;
private int default_port;
KJASSimpleURLConnection(URL u, int p) {
super(u);
default_port = p;
}
public boolean usingProxy() {
return false; // FIXME
}
public Permission getPermission() throws IOException {
int p = url.getPort();
if (p < 0)
p = default_port;
return new SocketPermission(url.getHost() + ":" + p, "connect");
}
public void connect() throws IOException {
if (kioconnection != null)
return;
Main.debug ("TDEIO KJASSimpleURLConnection.connection " + url);
SecurityManager security = System.getSecurityManager();
if (security != null)
security.checkPermission(getPermission());
kioconnection = new KIOSimpleConnection(url);
kioconnection.connect(doInput);
connected = true;
}
public void disconnect() {
if (kioconnection == null)
return;
kioconnection.disconnect();
kioconnection = null;
connected = false;
}
public InputStream getInputStream() throws IOException {
doInput = true;
doOutput = false;
if (kioconnection == null)
connect();
return kioconnection.getInputStream();
}
public OutputStream getOutputStream() throws IOException {
doInput = false;
doOutput = true;
if (kioconnection == null)
connect();
return kioconnection.getOutputStream();
}
}
final class KJASHttpURLStreamHandler extends URLStreamHandler
{
KJASHttpURLStreamHandler(int port) {
default_port = port;
}
protected URLConnection openConnection(URL u) throws IOException {
URL url = new URL(u.toExternalForm());
return new KJASHttpURLConnection(url);
}
protected int getDefaultPort() {
return default_port;
}
private int default_port;
}
final class KJASSimpleURLStreamHandler extends URLStreamHandler
{
KJASSimpleURLStreamHandler(int port) {
default_port = port;
}
protected URLConnection openConnection(URL u) throws IOException {
URL url = new URL(u.toExternalForm());
return new KJASSimpleURLConnection(url, default_port);
}
protected int getDefaultPort() {
return default_port;
}
private int default_port;
}
public final class KJASURLStreamHandlerFactory
implements URLStreamHandlerFactory
{
public URLStreamHandler createURLStreamHandler(String protocol) {
if (protocol.equals("jar") || protocol.equals("file"))
return null;
//outputs to early: Main.debug ("createURLStreamHandler " + protocol);
Main.debug ("TDEIO createURLStreamHandler " + protocol);
if (protocol.equals("http"))
return new KJASHttpURLStreamHandler(80);
else if (protocol.equals("https"))
return new KJASHttpURLStreamHandler(443);
else if (protocol.equals("ftp"))
return new KJASSimpleURLStreamHandler(21);
else if (protocol.equals("smb"))
return new KJASSimpleURLStreamHandler(139);
else if (protocol.equals("fish"))
return new KJASSimpleURLStreamHandler(22);
return null;
}
}