Revision: 4902 http://sourceforge.net/p/vexi/code/4902 Author: mkpg2 Date: 2016-11-23 20:34:50 +0000 (Wed, 23 Nov 2016) Log Message: ----------- Change! and Improvement. Pass listener to pipe method instead of traps on the inputstream. - this will break file uploads in older vexi code. Improve. Post handling. Previously progress bar useless as data very quickly buffered and then slowly uploaded (in real usage). Removed org.ibex.io project (unused). Removed old HTTP implementation (unmaintained/obselete). Improve IOUtil.pipe. Move output.close() outside of synchronization to allow recursive call during close (done by http).
Modified Paths: -------------- branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/core/Main.java branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/graphics/PNG.java branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/plat/Platform.java branches/vexi3/org.vexi-library.js/meta/module.xml branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Fountain.java branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/JSU.java branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Scheduler.java branches/vexi3/org.vexi-library.js/src/main/jpp/org/vexi/js/VexiJS.jpp branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/JSTestUtil.java branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/RunJS.java branches/vexi3/org.vexi-library.js/src/test/java/org/ibex/js/TestFountain.java branches/vexi3/org.vexi-library.js/src/test/java/test/js/exec/JSTestCase.java branches/vexi3/org.vexi-library.net/src/main/java/org/ibex/net/HTTP.java branches/vexi3/org.vexi-library.net/src/main/java/org/ibex/net/JreHTTP.java trunk/org.vexi-build.jpp/src/poke/java/poke/PreprocessHttpFactory.java trunk/org.vexi-build.shared/meta/module.revisions trunk/org.vexi-library.util/src/main/java/org/ibex/util/Callable.java trunk/org.vexi-library.util/src/main/java/org/ibex/util/IOUtil.java Added Paths: ----------- branches/vexi3/org.vexi-library.js/src/poke/java/poke/upload/ branches/vexi3/org.vexi-library.js/src/poke/java/poke/upload/PokeDataUpload.java trunk/org.vexi-library.util/src/main/java/org/ibex/util/Callback.java Removed Paths: ------------- branches/vexi3/org.vexi-library.net/src/main/java/org/ibex/net/OrigHTTP.java branches/vexi3/org.vexi-library.net/src/main/java/org/ibex/net/OrigHTTP_reconnect.java Modified: branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/core/Main.java =================================================================== --- branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/core/Main.java 2016-11-18 12:45:45 UTC (rev 4901) +++ branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/core/Main.java 2016-11-23 20:34:50 UTC (rev 4902) @@ -17,7 +17,6 @@ import org.ibex.util.Callable; import org.ibex.util.Encode; import org.ibex.util.Logger; -import org.vexi.graphics.Picture; import org.vexi.plat.Platform; import org.vexi.util.Log; @@ -312,10 +311,10 @@ this.data = data; this.name = name; } - public InputStream _getInputStream(boolean expect) throws IOException { + @Override public InputStream getInputStream(boolean expect) throws IOException { return Encode.JavaSourceCode.decode(data); } - public String coerceToString() { + @Override public String coerceToString() { return name!=null?name:super.coerceToString(); } } Modified: branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/graphics/PNG.java =================================================================== --- branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/graphics/PNG.java 2016-11-18 12:45:45 UTC (rev 4901) +++ branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/graphics/PNG.java 2016-11-23 20:34:50 UTC (rev 4902) @@ -265,11 +265,11 @@ int upix[] = null; int temp[] = new int[rowSize]; //int nextY = sRow; // next Y value and number of rows to report to sendPixels - int rows = 0; +// int rows = 0; int rowStart = sRow * p.width; for (int y = sRow; y < p.height; y += rInc, rowStart += sInc) { - rows += rInc; +// rows += rInc; int rowFilter = dis.read(); dis.readFully(inbuf); if (!filterRow(inbuf, pix, upix, rowFilter, filterOffset)) { Modified: branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/plat/Platform.java =================================================================== --- branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/plat/Platform.java 2016-11-18 12:45:45 UTC (rev 4901) +++ branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/plat/Platform.java 2016-11-23 20:34:50 UTC (rev 4902) @@ -6,14 +6,11 @@ import java.awt.Rectangle; import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import org.ibex.js.JS; import org.ibex.js.JSExn; -import org.ibex.util.Encode; import org.ibex.util.Logger; import org.vexi.core.Box; import org.vexi.core.Constants; Modified: branches/vexi3/org.vexi-library.js/meta/module.xml =================================================================== --- branches/vexi3/org.vexi-library.js/meta/module.xml 2016-11-18 12:45:45 UTC (rev 4901) +++ branches/vexi3/org.vexi-library.js/meta/module.xml 2016-11-23 20:34:50 UTC (rev 4902) @@ -6,7 +6,6 @@ <dependencies> <dependency source="local" name="library.value"/> <dependency source="local" name="library.net"/> - <dependency source="local" name="library.io"/> <!-- Test Dependencies --> <dependency source="local" name="library.testing" branch="trunk" scope="test"/> <dependency source="ibiblio" org="org.apache.xmlrpc" name="xmlrpc-server" tag="3.1.3" scope="test"/> Modified: branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Fountain.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Fountain.java 2016-11-18 12:45:45 UTC (rev 4901) +++ branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Fountain.java 2016-11-23 20:34:50 UTC (rev 4902) @@ -9,7 +9,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.FilterInputStream; +import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -29,6 +29,7 @@ import org.ibex.net.HTTPFactory; import org.ibex.util.Cache; import org.ibex.util.Callable; +import org.ibex.util.Callback; import org.ibex.util.IOUtil; import org.ibex.util.Logger; @@ -46,16 +47,27 @@ */ public abstract class Fountain extends JS.Obj implements JS.Cloneable, Constants { + public Fountain() {} // Public Interface ////////////////////////////////////////////////////////////////////////////// - static public class NotCacheableException extends Exception { } + static public class Info { + public String name; + public Long length; + + public JS asJS(){ + JS.Obj r = new JS.Obj(); + r.putSafe(JSU.S("name"), JSU.S(name)); + r.putSafe(JSU.S("length"), JSU.N(length)); + return r; + + } + } // streams are "sealed" by default to prevent accidental object leakage private Cache getCache = new Cache(100); - private Scheduler scheduler; - - public final JS get(JS key) throws JSExn { + + @Override public final JS get(JS key) throws JSExn { JS ret = (JS)getCache.get(key); if (ret == null) getCache.put(key, ret = _get(key)); return ret; @@ -79,85 +91,18 @@ return r==null?null:r.findWrite(); } - final public InputStream getInputStream(boolean expect) throws IOException{ return getInputStream(this, expect); } - final public InputStream getInputStream(final Fountain principal, boolean expect) throws IOException { - try{ - // HACK - there is some race potential here, but only if - // the programmer decides to try and remove/change these traps - // after accessing the stream. - InputStream is = _getInputStream(expect); - if(is==null) return is; - Trap startTrap = principal.wtrap(SC_start); - final Trap progressTrap = principal.wtrap(SC_progress); - final Trap finishTrap = principal.wtrap(SC_finish); - if(startTrap==null && progressTrap==null && finishTrap==null) return is; - - // HMMM the scheduler stuff is no so clean, perhaps a rethink on - // how streams are watched or other change needed. - final Scheduler sched = principal.scheduler; - - JS info = getInfo(); - // detirmine the callback rate - int length = JSU.toInt(info.get(SC_length)); - final int callbackRate = Math.max(8192,length/100); - - if(startTrap!=null){ - sched.scheduleJustTriggerTraps(principal, SC_start, info); - } - - return new FilterInputStream(is) { - private int bytesDownloaded = 0; - private int bytesSinceCallback = 0; - public int read() throws IOException { - int ret = super.read(); - if (ret != -1) triggerProgress(1); - else triggerFinish(); - return ret; - } - public int read(byte[] b, int off, int len) throws IOException { - int ret = super.read(b, off, len); - if (ret != -1) - triggerProgress(ret); - else - triggerFinish(); - return ret; - } - private void triggerProgress(int n){ - bytesDownloaded += n; bytesSinceCallback += n; - if(bytesSinceCallback>callbackRate){ - bytesSinceCallback-=callbackRate; - // FEATURE - if not run yet, update previous - // scheduled JS arg. - if(progressTrap!=null) sched.scheduleJustTriggerTraps(principal, SC_progress, JSU.N(bytesDownloaded)); - } - } - - private void triggerFinish(){ - if(finishTrap!=null) sched.scheduleJustTriggerTraps(principal, SC_finish, JSU.T); - } - }; - }catch(JSExn e){ - // only supported by Java 6 (!) - //throw new IOException(e); - throw new IOException(e.getMessage()); - } + public InputStream getInputStream(boolean expect) throws IOException{ + if(expect) + throw new IOException(toString() + " is write only"); + return null; } - - abstract protected InputStream _getInputStream(boolean expect) throws IOException; - public OutputStream getOutputStream(boolean expect) throws IOException{ + public OutputStream getOutputStream(boolean expect, Long contentLength) throws IOException{ if(expect) throw new IOException(toString() + " is read only"); return null; } - public void addTrap(JS key, JS f) throws JSExn { - super.addTrap(key, f); - // used for call backs when input stream is read, we assume - // the traps added are callbacks - if(scheduler==null) scheduler = Scheduler.expectCurrent(); - } - // Perform any caching (Fountain.Multi calls this on its // constituent streams so that it can cache everything up // front). REMARK - do we need this? can't we just make sure @@ -167,15 +112,15 @@ // Info about the stream (lastModified, contentLength, url ...) // HACK - optionally takes the stream for HTTP to avoid making two calls - public JS getInfo() throws IOException { return new JS.Obj();} + public Info getInfo() throws IOException { return new Info(); } - public JS callMethod(JS this_, JS method, JS[] args) throws JSExn { + @Override public JS callMethod(JS this_, JS method, JS[] args) throws JSExn { if("info".equals(JSU.toString(method))){ try { final Scheduler sched = Scheduler.expectCurrent(); return sched.backgroundCall("get fountain info", new Callable<Object, JS>() { public JS run(Object A) throws Exception { - return getInfo(); + return getInfo().asJS(); } }); } catch (JSExn e) { @@ -206,36 +151,37 @@ this.url = url; } private org.ibex.net.HTTP http() throws IOException{ return HTTPFactory.create(logger,url);} - public String canonical() { return url; } + @Override public String canonical() { return url; } - public JS _get(JS key) throws JSExn { return new HTTP(logger,url + "/" + JSU.toString(key)); } - private JS readInfo(HTTPEntityInfo info){ - JS.Obj r = new JS.Obj(); - r.putSafe(SC_lastModified, JSU.S(info.lastModified)); - r.putSafe(SC_length, JSU.N(info.contentLength)); - r.putSafe(SC_mimetype, JSU.S(info.contentType)); - r.putSafe(SC_name, JSU.S(url)); - return r; + @Override public JS _get(JS key) throws JSExn { return new HTTP(logger,url + "/" + JSU.toString(key)); } + private Info readInfo(HTTPEntityInfo info){ + Info r = new Info(); + r.name = url; + r.length = info.contentLength; + return r; +// r.putSafe(SC_lastModified, JSU.S(info.lastModified)); +// r.putSafe(SC_mimetype, JSU.S(info.contentType)); } public HTTPEntityInfo getRawInfo() { return responseInfo; } - public JS getInfo() throws IOException { + + private void useResponse() throws IOException{ if(responseInfo==null){ HTTPResponse response = http().GET(); responseStream = response.body; responseInfo = response.info;; } + } + + @Override public Info getInfo() throws IOException { + useResponse(); return readInfo(responseInfo); } //public String getCacheKey(Vec path) throws NotCacheableException { return url; } - public InputStream _getInputStream(boolean expect) throws IOException { - if(responseStream==null){ - HTTPResponse response = http().GET(); - responseStream = response.body; - responseInfo = response.info; - } + @Override public InputStream getInputStream(boolean expect) throws IOException { + useResponse(); InputStream r = responseStream; responseStream = null; return r; @@ -245,21 +191,22 @@ // if the server doesn't support HTTP/HEAD fallback to get catch(HTTPException e){return http.GET(null, null);} }*/ - public OutputStream getOutputStream(boolean expect) { - // ALL WRONG - // HACK - we need a way to retrieve the output stream - // from the http post - ByteArrayOutputStream r = new ByteArrayOutputStream(){ - public void close() throws IOException { - HTTPResponse response = http().POST(mimetype, toByteArray()); - responseStream = response.body; - responseInfo = response.info; - } - }; - return r; + @Override public OutputStream getOutputStream(boolean expect, Long contentLength) throws IOException { + String contentType = "".equals(mimetype)?"application/octet-stream":mimetype; + return http().POST2(contentType, contentLength, new Callback() { + public void run(Object arg) { + if(arg instanceof HTTPResponse){ + HTTPResponse response = (HTTPResponse)arg; + responseStream = response.body; + responseInfo = response.info; + }else{ + logger.warn(HTTP.class, arg); + } + } + }); } - public JS getResponseInfo(){ return readInfo(responseInfo); } + public JS getResponseInfo(){ return readInfo(responseInfo).asJS(); } public void setRequestInfo(JS info) throws JSExn { Enumeration en = info.keys().iterator(); @@ -276,8 +223,8 @@ public static class Resource extends Fountain { private final URL res; public Resource(URL res) { this.res = res; } - public InputStream _getInputStream(boolean expect) throws IOException { return res.openStream(); } - public String coerceToString(){ return res.toString(); } + @Override public InputStream getInputStream(boolean expect) throws IOException { return res.openStream(); } + @Override public String coerceToString(){ return res.toString(); } } /** byte arrays */ @@ -288,8 +235,14 @@ public ByteArray(JS suggested) throws JSExn { this.suggested = suggested==null?8192:JSU.toInt(suggested); } - public InputStream _getInputStream(boolean expect) throws IOException { return new ByteArrayInputStream(bytes); } - public OutputStream getOutputStream(boolean expect) throws IOException { + @Override public Info getInfo() throws IOException { + Info r = new Info(); + if(bytes!=null) r.length = (long)bytes.length; + return r; + } + + @Override public InputStream getInputStream(boolean expect) throws IOException { return new ByteArrayInputStream(bytes); } + public OutputStream getOutputStream(boolean expect, Integer contentLength) throws IOException { return new ByteArrayOutputStream(suggested){ public void close() throws IOException { super.close(); @@ -325,17 +278,15 @@ this.writeable = writeable; this.checkCase = checkCase; } - public String canonical() { return "file://"+path; } - public JS getInfo() throws IOException { + @Override public String canonical() { return "file://"+path; } + @Override public Info getInfo() throws IOException { java.io.File f = new java.io.File(path); - JS.Obj r = new JS.Obj(); - //r.putSafe(SC_lastModified, JSU.S(f.lastModified())); - //r.putSafe(SC_mimetype, JSU.S(info.contentType)); - r.putSafe(SC_length, JSU.N(f.length())); - r.putSafe(SC_name, JSU.S(canonical())); - return r; + Info r = new Info(); + r.name = canonical(); + r.length = f.length(); + return r; } - public InputStream _getInputStream(boolean expect) throws IOException { + @Override public InputStream getInputStream(boolean expect) throws IOException { try{ return new FileInputStream(path); }catch(FileNotFoundException e){ @@ -343,21 +294,21 @@ return null; } } - public OutputStream getOutputStream(boolean expect) throws IOException { + @Override public OutputStream getOutputStream(boolean expect, Long contentLength) throws IOException { if(!writeable) { if(expect) throw new IOException("File readonly: " + coerceToString()); return null; } return new FileOutputStream(path); } - public JS _get(JS key) throws JSExn { return new File(path + java.io.File.separatorChar + JSU.toString(key), writeable, checkCase); } + @Override public JS _get(JS key) throws JSExn { return new File(path + java.io.File.separatorChar + JSU.toString(key), writeable, checkCase); } public void remove() throws JSExn { if(writeable){ new java.io.File(path).delete(); }else throw new JSExn("not writeable " + writeable); } - Set _getKeySet() throws IOException { + @Override Set _getKeySet() throws IOException { Set s = new HashSet(); java.io.File f = new java.io.File(path); if(f.isDirectory()){ @@ -369,9 +320,9 @@ return s; } private java.io.File file(){ return new java.io.File(path); } - public long getTimestamp() { return file().lastModified(); } - public int hashCode() { return file().hashCode(); } - public boolean equals(Object obj) { + @Override public long getTimestamp() { return file().lastModified(); } + @Override public int hashCode() { return file().hashCode(); } + @Override public boolean equals(Object obj) { if(!(obj instanceof File)) return false; return file().equals(((File)obj).file()); } @@ -408,20 +359,64 @@ } } - static final public int[] ARGTYPES_afountain = new int[]{JSU.FOUNTAIN}; static public String nameOf(JS[] args) throws JSExn{ - JSU.checkArgs(args, ARGTYPES_afountain); - return ((Fountain)args[0]).canonical(); + Fountain arg = JSU.expectArg_fountain(args, 0); + return arg.canonical(); } - static final int[] ARGTYPES_2fountains = new int[]{JSU.FOUNTAIN,JSU.FOUNTAIN}; static public void pipe(final JS[] args) throws JSExn{ - JSU.checkArgs(args, ARGTYPES_2fountains); + final Fountain f0 = JSU.expectArg_fountain(args, 0); + final Fountain f1 = JSU.expectArg_fountain(args, 1); + final JS listener = JSU.getArg(args, 2); final Scheduler sched = Scheduler.expectCurrent(); + sched.backgroundCall("pipe streams", new Callable<Object, JS>() { public JS run(Object A) throws Exception { try { - IOUtil.pipe(((Fountain)args[0]).getInputStream(true), ((Fountain)args[1]).getOutputStream(true)); + Info info = f0.getInfo(); + Long contentLength = info.length; + OutputStream os =f1.getOutputStream(true, contentLength); + if(listener!=null){ + + long length = contentLength==null?819200:contentLength; + final int callbackRate = (int)Math.max(8192,length/100); + + sched.schedulePutAndTriggerTraps(listener, SC_start, info.asJS()); + + os = new FilterOutputStream(os) { + private int bytesDownloaded = 0; + private int bytesSinceCallback = 0; + + public void write(int b) throws IOException { + out.write(b); + triggerProgress(1); + } + public void write(byte[] b, int off, int len) throws IOException { + out.write(b, off, len); + triggerProgress(len); + } + + private void triggerProgress(int n) throws IOException{ + bytesDownloaded += n; bytesSinceCallback += n; + if(bytesSinceCallback>callbackRate){ + bytesSinceCallback-=callbackRate; + // FEATURE - if not run yet, update previous + // scheduled JS arg. + sched.schedulePutAndTriggerTraps(listener, SC_progress, JSU.N(bytesDownloaded)); + } + } + + public void close() throws IOException { + super.close(); + sched.schedulePutAndTriggerTraps(listener, SC_finish, JSU.T); + } + }; + } + + + // REMARK need our own buffer, since pipe can be recurvively called with HTTP stuff (reading response +// byte[] buffer = new byte[16*1024]; + IOUtil.pipe(f0.getInputStream(true), os); return null; } catch (IOException e) { throw JSU.handleFountainExn(e); @@ -480,7 +475,7 @@ } //public String getCacheKey() throws NotCacheableException { return parent.getCacheKey() + "!zip:"; } public JS _get(JS key) throws JSExn { return new ZipFile(parent, path==null?JSU.toString(key):path+'/'+JSU.toString(key)); } - public InputStream _getInputStream(boolean expect) throws IOException { + public InputStream getInputStream(boolean expect) throws IOException { ZipEntry entry = parent.getEntry(path); if (entry == null) throw new IOException("requested file (" + path + ") not found in archive"); return parent.getInputStream(entry); @@ -537,8 +532,8 @@ this.path = path; } //public String getCacheKey() throws NotCacheableException { return parent.getCacheKey() + "!zip:"; } - public JS _get(JS key) throws JSExn { return new ZipStream(parent, path==null?JSU.toString(key):path+'/'+JSU.toString(key)); } - public InputStream _getInputStream(boolean expect) throws IOException { + @Override public JS _get(JS key) throws JSExn { return new ZipStream(parent, path==null?JSU.toString(key):path+'/'+JSU.toString(key)); } + @Override public InputStream getInputStream(boolean expect) throws IOException { InputStream pis = parent.getInputStream(expect); ZipInputStream zis = new ZipInputStream(pis); ZipEntry ze = zis.getNextEntry(); @@ -627,14 +622,14 @@ throw new JSExn(e.getMessage());} return new Multiple(fountains, key, path); } - public InputStream _getInputStream(boolean expect) throws IOException { + public InputStream getInputStream(boolean expect) throws IOException { InputStream is; // FIXME: consider not doing loop when chosen!=null for (int i=0; i< fountains.size(); i++) { Fountain stream = (Fountain)fountains.get(i); if (stream!=null) { try { - is = stream.getInputStream(this, expect); + is = stream.getInputStream(expect); } catch (FileNotFoundException e) { is=null; } catch (IOException e) { is=null; } Modified: branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/JSU.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/JSU.java 2016-11-18 12:45:45 UTC (rev 4901) +++ branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/JSU.java 2016-11-23 20:34:50 UTC (rev 4902) @@ -4,13 +4,19 @@ package org.ibex.js; -import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.nio.charset.Charset; import org.ibex.js.JS.ConstructorList; -import org.ibex.js.parse.*; -import org.ibex.net.HTTP.*; -import org.ibex.util.*; +import org.ibex.js.parse.ByteCodes; +import org.ibex.js.parse.Function; +import org.ibex.js.parse.Lexer; +import org.ibex.js.parse.Tokens; +import org.ibex.net.HTTP.HTTPErrorResponse; +import org.ibex.util.Basket; +import org.ibex.util.XML; import org.vexi.value.Rational; public class JSU implements Constants{ @@ -71,7 +77,7 @@ static public OutputStream getOutputStream(JS js) throws IOException { Fountain f = getFountain(js); - return (f!=null)?f.getOutputStream(false):null; + return (f!=null)?f.getOutputStream(false,null):null; } // HACK - this is an unideal way to handle it, as it relies @@ -365,6 +371,19 @@ return r; } + static public Fountain getArg_fountain(JS[] args, int index) throws JSExn { + JS r = getArg(args, index); + if(r==null) return null; + Fountain js = getFountain(r); + if(js==null) throw new JSExn("Arg "+index+". Expected fountain, got "+r); + return js; + } + static public Fountain expectArg_fountain(JS[] args, int index) throws JSExn { + Fountain r = getArg_fountain(args, index); + if(r==null) throw new JSExn("Arg "+index+". Expected fountain, got null"); + return r; + } + /** lets us put multi-level get/put/call keys all in the same method */ static public class Sub extends JS.Obj { final public JS main; Modified: branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Scheduler.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Scheduler.java 2016-11-18 12:45:45 UTC (rev 4901) +++ branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Scheduler.java 2016-11-23 20:34:50 UTC (rev 4902) @@ -485,7 +485,7 @@ } } - public void scheduleJustTriggerTraps(final JS.Obj obj, final JS key, final JS value) { + public void scheduleJustTriggerTraps(final JS obj, final JS key, final JS value) { add(new Callable() { public Object run(Object o) throws Exception { justTriggerTraps(obj, key, value); @@ -494,6 +494,16 @@ }); } + public void schedulePutAndTriggerTraps(final JS obj, final JS key, final JS value) { + add(new Callable() { + public Object run(Object o) throws Exception { + putAndTriggerTraps(obj, key, value); + return null; + } + }); + } + + public void schedule(final Callable callback, final Object response) { add(new Callable() { // Optimise this object creation away by adding set Modified: branches/vexi3/org.vexi-library.js/src/main/jpp/org/vexi/js/VexiJS.jpp =================================================================== --- branches/vexi3/org.vexi-library.js/src/main/jpp/org/vexi/js/VexiJS.jpp 2016-11-18 12:45:45 UTC (rev 4901) +++ branches/vexi3/org.vexi-library.js/src/main/jpp/org/vexi/js/VexiJS.jpp 2016-11-23 20:34:50 UTC (rev 4902) @@ -488,7 +488,6 @@ } case 2: { //#switch(JSU.toString(method)) - case "pipe": Fountain.pipe(args); return null; case "parsexml": JSU.checkArgs(args, ARGTYPES_parsexml); new XMLHelper(scheduler(),args[1]).doParse(args[0]); @@ -499,6 +498,7 @@ //#switch(JSU.toString(method)) case "buffer": return new Fountain.ByteArray(args.length>0?args[0]:null); case "multiple": return Fountain.multiple(args); + case "pipe": Fountain.pipe(args); return null; //#end return super.callMethod(this_, method, args); } @@ -698,11 +698,10 @@ } static private JS new_utf8writer(JS[] args) throws JSExn { - JSU.checkArgs(args, Fountain.ARGTYPES_afountain); - Fountain f = (Fountain)args[0]; + Fountain f = JSU.expectArg_fountain(args, 0); final BufferedWriter w; try { - w = new BufferedWriter(new OutputStreamWriter(f.getOutputStream(true), "UTF8")); + w = new BufferedWriter(new OutputStreamWriter(f.getOutputStream(true, null), "UTF8")); } catch (UnsupportedEncodingException e) { throw new Error("UF8 not available!"); } catch (IOException e) { @@ -924,7 +923,8 @@ static public JS new_xmlwriter(JS[] args) throws JSExn { - JSU.checkArgs(args, Fountain.ARGTYPES_afountain); + + JSU.expectArg(args, 0); try { final BufferedWriter out = new BufferedWriter(new OutputStreamWriter(JSU.getOutputStream(args[0]))); JS writer = new JS.Immutable() { Modified: branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/JSTestUtil.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/JSTestUtil.java 2016-11-18 12:45:45 UTC (rev 4901) +++ branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/JSTestUtil.java 2016-11-23 20:34:50 UTC (rev 4902) @@ -4,17 +4,16 @@ import java.io.FilenameFilter; import java.lang.reflect.Method; import java.net.URISyntaxException; -import java.net.URL; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; -import junit.framework.TestSuite; - import org.ibex.js.JS.Enumeration; import org.ibex.js.JS.Keys; import org.vexi.testutil.JUnitUtil; +import junit.framework.TestSuite; + public class JSTestUtil { static private File guessWorkspaceDirectorForEclEmma(File dir){ while( !dir.getName().equals(".metadata")){ @@ -119,8 +118,8 @@ try { Class klass = Class.forName(cfullname); try{ - Method m = (Method)klass.getMethod("suite", null); - csuite = (TestSuite)m.invoke(null, null); + Method m = (Method)klass.getMethod("suite"); + csuite = (TestSuite)m.invoke(null); break; }catch(NoSuchMethodException e){continue;} //klass.getSuperclass() Modified: branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/RunJS.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/RunJS.java 2016-11-18 12:45:45 UTC (rev 4901) +++ branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/RunJS.java 2016-11-23 20:34:50 UTC (rev 4902) @@ -159,7 +159,7 @@ static public boolean finishedCleanly() throws Exception{ for(int i=10; i>0; i--){ - if(!SCHEDULER.scheduleWakeUp.isAlive()) return true; + if(!Scheduler.scheduleWakeUp.isAlive()) return true; java.lang.Thread.sleep(100); } return false; Added: branches/vexi3/org.vexi-library.js/src/poke/java/poke/upload/PokeDataUpload.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/poke/java/poke/upload/PokeDataUpload.java (rev 0) +++ branches/vexi3/org.vexi-library.js/src/poke/java/poke/upload/PokeDataUpload.java 2016-11-23 20:34:50 UTC (rev 4902) @@ -0,0 +1,90 @@ +package poke.upload; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.ibex.js.Fountain.HTTP; +import org.ibex.js.JS; +import org.ibex.js.JSArray; +import org.ibex.js.JSExn; +import org.ibex.js.JSU; +import org.ibex.js.JSUX; +import org.ibex.js.XMLRPC; +import org.ibex.util.DefaultLog; +import org.ibex.util.IOUtil; + +public class PokeDataUpload { + static public void main(String[] args) throws JSExn, IOException { + String password = args[0]; + + // log in + XMLRPC xmlrpc = new XMLRPC(DefaultLog.logger, "http://synergetixtest.statii.co.uk/xmlrpc", "emanate.ApplicationPublic.login"); + List args1 = new ArrayList(); + Collections.addAll(args1, new String[]{"%statii/Statii%", null, "m...@synergetix.eu", password}); + JSArray args2 = (JSArray)JSUX.dynamicToJs(args1); + JS r = xmlrpc.doCall(args2); + String session = ""+r.get(JSU.S("sessionId")); + System.err.println("!!"+session); + + String uploadUrl = "http://synergetixtest.statii.co.uk/blob?session="+session+"&name=tttttt"; + // + HTTP upload = new HTTP(DefaultLog.logger, uploadUrl); + + final long contentLength = 1000000; + InputStream is = new InputStream() { + private long count = contentLength; + public int read() throws IOException { + count--; + if(count<0){ + + return -1; + } + return (int)'t'; + } + }; + + long t0 = System.currentTimeMillis(); + final int callbackRate = 8192; + OutputStream os = new FilterOutputStream(upload.getOutputStream(true, contentLength)){ + private int bytesDownloaded = 0; + private int bytesSinceCallback = 0; + + @Override public void write(byte[] b, int off, int len) throws IOException { + super.write(b, off, len); + triggerProgress(len); + } + + @Override public void write(int b) throws IOException { + super.write(b); + triggerProgress(1); + } + @Override public void close() throws IOException { + super.close(); + triggerFinish(); + } + + private void triggerProgress(int n){ + bytesDownloaded += n; bytesSinceCallback += n; + if(bytesSinceCallback>callbackRate){ + bytesSinceCallback-=callbackRate; + // FEATURE - if not run yet, update previous + // scheduled JS arg. + System.err.println("Progress: "+bytesDownloaded); + } + } + + private void triggerFinish(){ + System.err.println("Finished"); + } + }; + IOUtil.pipe(is, os); + + long delta = System.currentTimeMillis()-t0; + System.err.println("Took "+delta+"ms"); + } +} Property changes on: branches/vexi3/org.vexi-library.js/src/poke/java/poke/upload/PokeDataUpload.java ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Modified: branches/vexi3/org.vexi-library.js/src/test/java/org/ibex/js/TestFountain.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/test/java/org/ibex/js/TestFountain.java 2016-11-18 12:45:45 UTC (rev 4901) +++ branches/vexi3/org.vexi-library.js/src/test/java/org/ibex/js/TestFountain.java 2016-11-23 20:34:50 UTC (rev 4902) @@ -3,18 +3,17 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; -import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.ibex.js.Fountain.Info; +import org.ibex.util.IOUtil; + import junit.framework.Assert; import junit.framework.TestCase; - -import org.ibex.util.IOUtil; - import test.servlets.SimpleContainer; public class TestFountain extends TestCase { @@ -28,16 +27,11 @@ Fountain a = new Fountain.ByteArray((JS)null); Fountain b = new Fountain.ByteArray((JS)null); Fountain.writeUTF8(new JS[]{a,JSU.S("sausage")}); - IOUtil.pipe(a.getInputStream(true), b.getOutputStream(true)); + IOUtil.pipe(a.getInputStream(true), b.getOutputStream(true, null)); String s = JSU.toString(Fountain.parseUTF8(new JS[]{b})); Assert.assertEquals("sausage",s); } - - private int expectInt(Object o){ - return ((Number)o).intValue(); - } - public void testEvents() throws Exception{ @@ -47,12 +41,10 @@ try{ Fountain f = new Fountain.HTTP(RunJS.LOG, "http://localhost:"+PORT+"/resource"); //InputStream i = f.getInputStream(); - JS infoJS = f.getInfo(); - Map info = (Map) JSPojoWrapper.jsToDynamic(infoJS); - //String lastModified = (String) info.get("lastModified"); - System.out.println(JSON.marshal(infoJS)); - assertEquals("http://localhost:9999/resource",info.get("name")); - assertEquals(53, expectInt(info.get("length"))); + Fountain.Info info = f.getInfo(); + long length = info.length; + assertEquals("http://localhost:9999/resource",info.name); + assertEquals(53l, length); }finally{ stopServer(); @@ -61,12 +53,11 @@ { Fountain f = new Fountain.File(TestFountain.class.getResource("resource.txt").getFile()); //InputStream i = a.getInputStream(); - JS infoJS = f.getInfo(); - Map info = (Map) JSPojoWrapper.jsToDynamic(infoJS); - System.out.println(JSON.marshal(infoJS)); - String name = (String) info.get("name"); + Info info = f.getInfo(); + String name = info.name; + long length = info.length; assertTrue(name.endsWith("resource.txt")); - assertEquals(53, expectInt(info.get("length"))); + assertEquals(53l, length); } } Modified: branches/vexi3/org.vexi-library.js/src/test/java/test/js/exec/JSTestCase.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/test/java/test/js/exec/JSTestCase.java 2016-11-18 12:45:45 UTC (rev 4901) +++ branches/vexi3/org.vexi-library.js/src/test/java/test/js/exec/JSTestCase.java 2016-11-23 20:34:50 UTC (rev 4902) @@ -1,13 +1,9 @@ package test.js.exec; -import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.io.Reader; -import junit.framework.AssertionFailedError; -import junit.framework.TestCase; - import org.ibex.js.DevUtil; import org.ibex.js.ExecParser; import org.ibex.js.Fountain; @@ -16,6 +12,9 @@ import org.ibex.js.RunJS; import org.ibex.js.parse.Function; +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; + /** * @author mike */ Modified: branches/vexi3/org.vexi-library.net/src/main/java/org/ibex/net/HTTP.java =================================================================== --- branches/vexi3/org.vexi-library.net/src/main/java/org/ibex/net/HTTP.java 2016-11-18 12:45:45 UTC (rev 4901) +++ branches/vexi3/org.vexi-library.net/src/main/java/org/ibex/net/HTTP.java 2016-11-23 20:34:50 UTC (rev 4902) @@ -1,16 +1,19 @@ package org.ibex.net; -import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; -import org.ibex.net.HTTP.HTTPSettings; +import org.ibex.util.Callback; public abstract class HTTP { static public class HTTPEntityInfo { - final public int contentLength; ///< the length of the entire content body; -1 if chunked + final public long contentLength; ///< the length of the entire content body; -1 if chunked final public String contentType; final public String lastModified; - HTTPEntityInfo(int contentLength, String lastModified, String contentType){ + HTTPEntityInfo(long contentLength, String lastModified, String contentType){ this.contentLength = contentLength; this.lastModified = lastModified; this.contentType = contentType; @@ -24,7 +27,7 @@ final public byte[] bytes; final public HTTPEntityInfo info; public HTTPErrorResponse(String msg, String code, byte[] bytes, HTTPEntityInfo info) { - super("HTTP Error: " + msg); + super("HTTP Error ("+code+"): " + msg); this.code = code; this.bytes = bytes; this.info = info; @@ -42,7 +45,7 @@ abstract public HTTPResponse GET() throws IOException; abstract public HTTPResponse POST(String mime, byte[] bytes) throws IOException; - + abstract public OutputStream POST2(String contentType, Long contentLength, Callback<Object> callback) throws IOException; static public class HTTPSettings { int connectTimeout; // in milliseconds @@ -72,4 +75,5 @@ settings.setConnectTimeout(ms); else settings = null; } + } Modified: branches/vexi3/org.vexi-library.net/src/main/java/org/ibex/net/JreHTTP.java =================================================================== --- branches/vexi3/org.vexi-library.net/src/main/java/org/ibex/net/JreHTTP.java 2016-11-18 12:45:45 UTC (rev 4901) +++ branches/vexi3/org.vexi-library.net/src/main/java/org/ibex/net/JreHTTP.java 2016-11-23 20:34:50 UTC (rev 4902) @@ -1,12 +1,14 @@ package org.ibex.net; +import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; +import org.ibex.util.Callback; import org.ibex.util.IOUtil; import org.ibex.util.Logger; @@ -89,6 +91,52 @@ } } + public OutputStream POST2(String contentType, Long contentLength, final Callback<Object> responseCallback) throws IOException { + try { + //Create connection + final HttpURLConnection connection = newConnection(); + if(contentLength!=null){ + // REMARK cannot do file upload if we do this +// connection.setRequestProperty("Content-Length", "" + contentLength); + connection.setFixedLengthStreamingMode(contentLength); + }else{ + connection.setChunkedStreamingMode(128*1024); + } + + + connection.setConnectTimeout(getConnectTimeout()); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", + contentType); + connection.setRequestProperty("Content-Language", "en-US"); + + connection.setUseCaches(false); + connection.setDoInput(true); + connection.setDoOutput(true); + + //Send request + OutputStream wr = new FilterOutputStream(connection.getOutputStream()){ + @Override public void write(byte[] b, int off, int len) throws IOException { + // nesessary to avoid doing 1 byte at a time + out.write(b, off, len); + } + + @Override public void close() throws IOException { + super.close(); + try{ + HTTPResponse response = readResponse(connection); + responseCallback.run(response); + }catch(Throwable t){ + responseCallback.run(t); + } + } + }; + return wr; + } catch (IOException e) { + throw e; + } + } + private HttpURLConnection newConnection() throws IOException{ URL url = new URL(this.url); return (HttpURLConnection)url.openConnection(); Deleted: branches/vexi3/org.vexi-library.net/src/main/java/org/ibex/net/OrigHTTP.java =================================================================== --- branches/vexi3/org.vexi-library.net/src/main/java/org/ibex/net/OrigHTTP.java 2016-11-18 12:45:45 UTC (rev 4901) +++ branches/vexi3/org.vexi-library.net/src/main/java/org/ibex/net/OrigHTTP.java 2016-11-23 20:34:50 UTC (rev 4902) @@ -1,1473 +0,0 @@ -// Copyright 2000-2008 the Contributors, as shown in the revision logs. -// Licensed under the Apache Software License 2.0 ("the License"). -// You may not use this file except in compliance with the License. - -package org.ibex.net; - -import java.net.*; -import java.io.*; -import java.util.*; - -import org.ibex.util.*; -import org.ibex.crypto.*; -/** - * This object encapsulates a *single* HTTP connection. Multiple requests may be pipelined over a connection (thread-safe), - * although any IOException encountered in a request will invalidate all later requests. - */ -public class OrigHTTP extends HTTP { - - public static InetAddress originAddr = null; - public static String originHost = null; - - // FIXME: HACK: these shouldn't be set globally - public static String userAgent = "Vexi"; - public static boolean allowRedirects = true; - - // Cookies ////////////////////////////////////////////////////////////////////////////// - - public static class Cookie { - public final String name; - public final String value; - public final String domain; - public final String path; - public final Date expires; - public final boolean secure; - public Cookie(String name, String value, String domain, String path, Date expires, boolean secure) { - this.name = name; - this.value = value; - this.domain = domain; - this.path = path; - this.expires = expires; - this.secure = secure; - } - - // FIXME: this could be much more efficient - // FIXME currently only implements http://wp.netscape.com/newsref/std/cookie_spec.html - public static class Jar { - private Hash h = new Hash(); - public String getCookieHeader(String domain, String path, boolean secure) { - StringBuffer ret = new StringBuffer("Cookie: "); - Enumeration e = h.keys(); - while (e.hasMoreElements()) { - Vec v = (Vec)h.get(e.nextElement()); - Cookie cookie = null; - for(int i=0; i<v.size(); i++) { - Cookie c = (Cookie)v.elementAt(i); - if (domain.endsWith(c.domain) && - (c.path == null || path.startsWith(c.path)) && - (cookie == null || c.domain.length() > cookie.domain.length())) - cookie = c; - } - if (cookie != null) { - ret.append(cookie.name); - ret.append("="); - ret.append(cookie.value); - ret.append("; "); - } - } - //ret.setLength(ret.length() - 2); - return ret.toString(); - } - public void setCookie(String header, String defaultDomain) { - String name = null; - String value = null; - String domain = defaultDomain; - String path = "/"; - Date expires = null; - boolean secure = false; - StringTokenizer st = new StringTokenizer(header, ";"); - while(st.hasMoreTokens()) { - String s = st.nextToken(); - if (s.indexOf('=') == -1) { - if (s.equals("secure")) secure = true; - continue; - } - String start = s.substring(0, s.indexOf('=')); - String end = s.substring(s.indexOf('=')+1); - if (name == null) { - name = start; - value = end; - continue; - } - String p = start.toLowerCase(); - if("domain".equals(p)) domain = end; - else if("path".equals(p)) path = end; - else if("expires".equals(p)) expires = new Date(end); - } - if (h.get(name) == null) h.put(name, new Vec()); - ((Vec)h.get(name)).addElement(new Cookie(name, value, domain, path, expires, secure)); - } - } - } - - // Public Methods //////////////////////////////////////////////////////////////////////////////////////// - public OrigHTTP(Logger logger, String url) { this(logger, url, false); } - public OrigHTTP(Logger logger, String url, boolean skipResolveCheck) { this.logger = logger; originalUrl = url; this.skipResolveCheck = skipResolveCheck; } - // HACK - public OrigHTTP(OrigHTTP old) { this(old.logger, old.originalUrl, old.skipResolveCheck); } - - - /** Performs an HTTP GET request - * @throws IOException */ - public HTTPResponse GET() throws IOException{ - return GET(null,null);} - public HTTPResponse GET(String referer, Cookie.Jar cookies) throws IOException { - return makeRequest("GET", null, referer, cookies); } - - public HTTPResponse HEAD(String referer, Cookie.Jar cookies) throws IOException { - return makeRequest("HEAD", null, referer, cookies); } - - /** Performs an HTTP POST request; content is additional headers, blank line, and body */ - public HTTPResponse POST(String contentType, byte[] content) throws IOException { - return makeRequest(contentType, content, null, null); } - public HTTPResponse POST(String contentType, byte[] content, String referer, Cookie.Jar cookies) throws IOException { - return makeRequest(contentType, content, referer, cookies); } - - public HTTPEntityInfo lastInfo(){ return lastInfo; } - - public static OrigHTTP stdio = new OrigHTTP(DefaultLog.logger,"stdio:"); - - - // Statics /////////////////////////////////////////////////////////////////////////////////////////////// - - static Hash resolvedHosts = new Hash(); ///< cache for resolveAndCheckIfFirewalled() - private static Hash authCache = new Hash(); ///< cache of userInfo strings, keyed on originalUrl - - - // Instance Data /////////////////////////////////////////////////////////////////////////////////////////////// - final Logger logger; - final String originalUrl; ///< the URL as passed to the original constructor; this is never changed - String url = null; ///< the URL to connect to; this is munged when the url is parsed */ - String host = null; ///< the host to connect to - int port = -1; ///< the port to connect on - boolean ssl = false; ///< true if SSL (HTTPS) should be used - String path = null; ///< the path (URI) to retrieve on the server - Socket sock = null; ///< the socket - InputStream in = null; ///< the socket's inputstream - String userInfo = null; ///< the username and password portions of the URL - boolean firstRequest = true; ///< true iff this is the first request to be made on this socket - boolean skipResolveCheck = false; ///< allowed to skip the resolve check when downloading PAC script - boolean proxied = false; ///< true iff we're using a proxy - - public HTTPEntityInfo lastInfo; - - /** this is null if the current request is the first request on - * this HTTP connection; otherwise it is a Semaphore which will be - * released once the request ahead of us has received its response - */ - Semaphore okToRecieve = null; - - /** - * This method isn't synchronized; however, only one thread can be in the inner synchronized block at a time, and the rest of - * the method is protected by in-order one-at-a-time semaphore lock-steps - */ - private HTTPResponse makeRequest(String contentType, byte[] content, String referer, Cookie.Jar cookies) throws IOException { - - // Step 1: send the request and establish a semaphore to stop any requests that pipeline after us - Semaphore blockOn = null; - Semaphore releaseMe = null; - synchronized(this) { - try { - connect(); - sendRequest(contentType, content, referer, cookies); - } catch (IOException e) { - reset(); - throw e; - } - blockOn = okToRecieve; - releaseMe = okToRecieve = new Semaphore(); - } - - // Step 2: wait for requests ahead of us to complete, then read the reply off the stream - boolean doRelease = true; - try { - if (blockOn != null) blockOn.block(); - - // previous call wrecked the socket connection, but we already sent our request, so we can't just retry -- - // this could cause the server to receive the request twice, which could be bad (think of the case where the - // server call causes Amazon.com to ship you an item with one-click purchasing). - if (in == null) - throw new HTTPException("a previous pipelined call messed up the socket"); - - Hashtable h = in == null ? null : parseHeaders(in, cookies); - if (h == null) { - if (firstRequest) throw new HTTPException("server closed the socket with no response"); - // sometimes the server chooses to close the stream between requests - reset(); - releaseMe.release(); - return makeRequest(contentType, content, referer, cookies); - } - - String reply = h.get("STATUSLINE").toString(); - - if (reply.startsWith("407") || reply.startsWith("401")) { - - if (reply.startsWith("407")) doProxyAuth(h, content == null ? "GET" : "POST"); - else doWebAuth(h, content == null ? "GET" : "POST"); - - if (h.get("HTTP").equals("1.0") && h.get("content-length") == null) { - logger.info(this, "proxy returned an HTTP/1.0 reply with no content-length..."); - reset(); - } else { - newHTTPInputStream(in, h, releaseMe).close(); - } - releaseMe.release(); - return makeRequest(contentType, content, referer, cookies); - - } else if (reply.startsWith("3") && allowRedirects) { - String location = (String)h.get("location"); - if (location == null) throw new HTTPException("Got HTTP " + reply.substring(0, 3) + " but no Location header"); - logger.info(HTTP.class, "redirecting to " + location); - if (content != null) - return new OrigHTTP(logger, location).POST(contentType, content, url, cookies); - else - return new OrigHTTP(logger, location).GET(url, cookies); - - } else if (reply.startsWith("2")) { - if (h.get("HTTP").equals("1.0") && h.get("content-length") == null) - throw new HTTPException("Vexi does not support HTTP/1.0 servers which fail to return the Content-Length header"); - HTTPInputStream ret = newHTTPInputStream(in, h, releaseMe); - doRelease = false; - return new HTTPResponse(ret.info,ret); - } else { - HTTPInputStream his = newHTTPInputStream(in, h, releaseMe); - // read into stream, so that we can release and are not obliged to handle - // the response (close the inputstream) - mike (assumes this is necessary) - ByteArrayOutputStream baos = new ByteArrayOutputStream(his.info.contentLength>0?his.info.contentLength:1024); - IOUtil.pipe(his, baos); - byte[] bytes = baos.toByteArray(); - //byte[] bytes = "{}".getBytes(); - his.close(); - throw new HTTPErrorResponse(reply, reply.substring(0,3), bytes, his.info); - } - - } catch (IOException e) { reset(); throw e; - } finally { if (doRelease) releaseMe.release(); - } - } - - - // Safeguarded DNS Resolver /////////////////////////////////////////////////////////////////////////// - - /** - * resolves the hostname and returns it as a string in the form "x.y.z.w" - * @throws HTTPException if the host falls within a firewalled netblock - */ - private void resolveAndCheckIfFirewalled(String host) throws HTTPException { - - // cached - if (resolvedHosts.get(host) != null) return; - - // if all scripts are trustworthy (local FS), continue - if (originAddr == null) return; - - // resolve using DNS - try { - InetAddress addr = InetAddress.getByName(host); - byte[] quadbyte = addr.getAddress(); - if ((quadbyte[0] == 10 || - (quadbyte[0] == 192 && quadbyte[1] == 168) || - (quadbyte[0] == 172 && (quadbyte[1] & 0xF0) == 16)) && !addr.equals(originAddr)) - throw new HTTPException("security violation: " + host + " [" + addr.getHostAddress() + - "] is in a firewalled netblock"); - return; - } catch (UnknownHostException uhe) { } - - /* - if (Platform.detectProxy() == null) - throw new HTTPException("could not resolve hostname \"" + host + "\" and no proxy configured"); - */ - } - - - // Methods to attempt socket creation ///////////////////////////////////////////////////////////////// - - private Socket getSocket(String host, int port, boolean ssl, boolean negotiate) throws IOException { - Socket ret = ssl ? new SSL(logger, host, port, negotiate) : new Socket(java.net.InetAddress.getByName(host), port); - ret.setTcpNoDelay(true); - return ret; - } - - /** Attempts a direct connection */ - private Socket attemptDirect() { - try { - logger.debug(this, "attempting to create unproxied socket to " + - host + ":" + port + (ssl ? " [ssl]" : "")); - return getSocket(host, port, ssl, true); - } catch (IOException e) { - logger.info(this, "exception in attemptDirect(): " + e); - return null; - } - } - - - // Everything Else //////////////////////////////////////////////////////////////////////////// - - private synchronized void connect() throws IOException { - if (originalUrl.equals("stdio:")) { in = new BufferedInputStream(System.in); return; } - if (sock != null) { - if (in == null) in = new BufferedInputStream(sock.getInputStream()); - return; - } - // grab the userinfo; gcj doesn't have java.net.URL.getUserInfo() - String url = originalUrl; - userInfo = url.substring(url.indexOf("://") + 3); - userInfo = userInfo.indexOf('/') == -1 ? userInfo : userInfo.substring(0, userInfo.indexOf('/')); - if (userInfo.indexOf('@') != -1) { - userInfo = userInfo.substring(0, userInfo.indexOf('@')); - url = url.substring(0, url.indexOf("://") + 3) + url.substring(url.indexOf('@') + 1); - } else { - userInfo = null; - } - - if (url.startsWith("https:")) { - ssl = true; - } else if (!url.startsWith("http:")) { - throw new IOException("HTTP only supports http/https urls"); - } - if (url.indexOf("://") == -1) throw new IOException("URLs must contain a ://"); - String temphost = url.substring(url.indexOf("://") + 3); - path = temphost.substring(temphost.indexOf('/')); - temphost = temphost.substring(0, temphost.indexOf('/')); - if (temphost.indexOf(':') != -1) { - port = Integer.parseInt(temphost.substring(temphost.indexOf(':')+1)); - temphost = temphost.substring(0, temphost.indexOf(':')); - } else { - port = ssl ? 443 : 80; - } - if (!skipResolveCheck) resolveAndCheckIfFirewalled(temphost); - host = temphost; - logger.debug(this, "creating HTTP object for connection to " + host + ":" + port); - - /* - Proxy pi = Platform.detectProxy(); - OUTER: do { - if (pi != null) { - for(int i=0; i<pi.excluded.length; i++) if (host.equals(pi.excluded[i])) break OUTER; - if (sock == null && pi.proxyAutoConfigFunction != null) sock = attemptPAC(pi.proxyAutoConfigFunction); - if (sock == null && ssl && pi.httpsProxyHost != null) sock = attemptHttpProxy(pi.httpsProxyHost,pi.httpsProxyPort); - if (sock == null && pi.httpProxyHost != null) sock = attemptHttpProxy(pi.httpProxyHost, pi.httpProxyPort); - if (sock == null && pi.socksProxyHost != null) sock = attemptSocksProxy(pi.socksProxyHost, pi.socksProxyPort); - } - } while (false); - */ - proxied = sock != null; - if (sock == null) sock = attemptDirect(); - if (sock == null) throw new HTTPException("unable to contact host " + host); - if (in == null) in = new BufferedInputStream(sock.getInputStream()); - } - - private void sendRequest(String contentType, byte[] content, String referer, Cookie.Jar cookies) throws IOException { - // REMARK - the BufferedOutputStream is necessary - for the xmlrpc tests at least. - // Not sure exactly why, but there were communication problems without it. - PrintStream ps = new PrintStream(new BufferedOutputStream( - originalUrl.equals("stdio:") ? - System.out : sock.getOutputStream())); - if (content != null) { - ps.print("POST " + path + " HTTP/1.0\r\n"); // FIXME chunked encoding - int contentLength = content.length; - ps.print("Content-Length: " + contentLength + "\r\n"); - if (contentType != null) ps.print("Content-Type: " + contentType + "\r\n"); - } else { - ps.print(contentType + " " + path + " HTTP/1.1\r\n"); - } - - if (cookies != null) ps.print(cookies.getCookieHeader(host, path, ssl)); - ps.print("User-Agent: " + userAgent + "\r\n"); - ps.print("Accept-encoding: gzip\r\n"); - ps.print("Host: " + (host + (port == 80 ? "" : (":" + port))) + "\r\n"); - if (proxied) ps.print("X-RequestOrigin: " + originHost + "\r\n"); - - if (Proxy.Authorization.authorization != null) ps.print("Proxy-Authorization: "+Proxy.Authorization.authorization2+"\r\n"); - if (authCache.get(originalUrl) != null) ps.print("Authorization: " + authCache.get(originalUrl) + "\r\n"); - - ps.print("\r\n"); - if(content!=null){ - ps.write(content); - } - ps.print("\r\n"); - ps.flush(); - } - - private void doWebAuth(Hashtable h0, String method) throws IOException { - if (userInfo == null) throw new HTTPException("web server demanded username/password, but none were supplied"); - Hashtable h = parseAuthenticationChallenge(h0.get("www-authenticate").toString()); - - if (h.get("AUTHTYPE").equals("Basic")) { - if (authCache.get(originalUrl) != null) throw new HTTPException("username/password rejected"); - authCache.put(originalUrl, "Basic " + new String(Encode.toBase64(userInfo.getBytes("UTF8")))); - - } else if (h.get("AUTHTYPE").equals("Digest")) { - if (authCache.get(originalUrl) != null && !"true".equals(h.get("stale"))) - throw new HTTPException("username/password rejected"); - String path2 = path; - if (path2.startsWith("http://") || path2.startsWith("https://")) { - path2 = path2.substring(path2.indexOf("://") + 3); - path2 = path2.substring(path2.indexOf('/')); - } - String A1 = userInfo.substring(0, userInfo.indexOf(':')) + ":" + h.get("realm") + ":" + - userInfo.substring(userInfo.indexOf(':') + 1); - String A2 = method + ":" + path2; - authCache.put(originalUrl, - "Digest " + - "username=\"" + userInfo.substring(0, userInfo.indexOf(':')) + "\", " + - "realm=\"" + h.get("realm") + "\", " + - "nonce=\"" + h.get("nonce") + "\", " + - "uri=\"" + path2 + "\", " + - (h.get("opaque") == null ? "" : ("opaque=\"" + h.get("opaque") + "\", ")) + - "response=\"" + H(H(A1) + ":" + h.get("nonce") + ":" + H(A2)) + "\", " + - "algorithm=MD5" - ); - - } else { - throw new HTTPException("unknown authentication type: " + h.get("AUTHTYPE")); - } - } - - private void doProxyAuth(Hashtable h0, String method) throws IOException { - if (logger.isInfo()) logger.info(this, "Proxy AuthChallenge: " + h0.get("proxy-authenticate")); - Hashtable h = parseAuthenticationChallenge(h0.get("proxy-authenticate").toString()); - String style = h.get("AUTHTYPE").toString(); - //String realm = (String)h.get("realm"); - - if (style.equals("NTLM") && Proxy.Authorization.authorization2 == null) { - logger.info(this, "Proxy identified itself as NTLM, sending Type 1 packet"); - Proxy.Authorization.authorization2 = "NTLM " + Encode.toBase64(Proxy.NTLM.type1); - return; - } - /* - if (!realm.equals("Digest") || Proxy.Authorization.authorization2 == null || !"true".equals(h.get("stale"))) - Proxy.Authorization.getPassword(realm, style, sock.getInetAddress().getHostAddress(), - Proxy.Authorization.authorization); - */ - if (style.equals("Basic")) { - Proxy.Authorization.authorization2 = - "Basic " + new String(Encode.toBase64(Proxy.Authorization.authorization.getBytes("UTF8"))); - - } else if (style.equals("Digest")) { - String A1 = Proxy.Authorization.authorization.substring(0, userInfo.indexOf(':')) + ":" + h.get("realm") + ":" + - Proxy.Authorization.authorization.substring(Proxy.Authorization.authorization.indexOf(':') + 1); - String A2 = method + ":" + path; - Proxy.Authorization.authorization2 = - "Digest " + - "username=\"" + Proxy.Authorization.authorization.substring(0, Proxy.Authorization.authorization.indexOf(':')) + - "\", " + - "realm=\"" + h.get("realm") + "\", " + - "nonce=\"" + h.get("nonce") + "\", " + - "uri=\"" + path + "\", " + - (h.get("opaque") == null ? "" : ("opaque=\"" + h.get("opaque") + "\", ")) + - "response=\"" + H(H(A1) + ":" + h.get("nonce") + ":" + H(A2)) + "\", " + - "algorithm=MD5"; - - } else if (style.equals("NTLM")) { - logger.info(this, "Proxy identified itself as NTLM, got Type 2 packet"); - byte[] type2 = Encode.fromBase64(((String)h0.get("proxy-authenticate")).substring(5).trim()); - for(int i=0; i<type2.length; i += 4) { - String log = ""; - if (i<type2.length) log += Integer.toString(type2[i] & 0xff, 16) + " "; - if (i+1<type2.length) log += Integer.toString(type2[i+1] & 0xff, 16) + " "; - if (i+2<type2.length) log += Integer.toString(type2[i+2] & 0xff, 16) + " "; - if (i+3<type2.length) log += Integer.toString(type2[i+3] & 0xff, 16) + " "; - logger.info(this, log); - } - // FEATURE: need to keep the connection open between type1 and type3 - // FEATURE: finish this - //byte[] type3 = Proxy.NTLM.getResponse( - //Proxy.Authorization.authorization2 = "NTLM " + Encode.toBase64(type3)); - } - } - - - // HTTPInputStream /////////////////////////////////////////////////////////////////////////////////// - - - /** An input stream that represents a subset of a longer input stream. Supports HTTP chunking as well */ - class HTTPInputStream extends FilterInputStream { - - - final public HTTPEntityInfo info; - - private int length = 0; ///< if chunking, numbytes left in this subset; else the remainder of the chunk - private Semaphore releaseMe = null; ///< this semaphore will be released when the stream is closed - private boolean chunkedDone = false; ///< indicates that we have encountered the zero-length terminator chunk - private boolean firstChunk = true; ///< if we're on the first chunk, we don't pre-read a CRLF - - - HTTPInputStream(InputStream in, Semaphore releaseMe, HTTPEntityInfo info) throws IOException { - super(in); - this.info = info; - this.releaseMe = releaseMe; - this.length = info.contentLength == -1 ? 0 : info.contentLength; - } - public boolean markSupported() { return false; } - public int read(byte[] b) throws IOException { return read(b, 0, b.length); } - public long skip(long n) throws IOException { return read(null, -1, (int)n); } - public int available() throws IOException { - if (info.contentLength == -1) return java.lang.Math.min(super.available(), length); - return super.available(); - } - - public int read() throws IOException { - byte[] b = new byte[1]; - int ret = read(b, 0, 1); - return ret == -1 ? -1 : b[0] & 0xff; - } - - private boolean isHexDigit(char c){ - return ('0' <= c && '9' >= c) || - ('a' <= c && 'f' >= c) || - ('A' <= c && 'F' >= c); - } - - private void readChunk() throws IOException { - if (chunkedDone) return; - if (!firstChunk) super.skip(2); // CRLF - firstChunk = false; - String chunkLen = ""; - while(true) { - int i = super.read(); - if (i == -1) throw new HTTPException("encountered end of stream while reading chunk length"); - - // FEATURE: handle chunking extensions - if (i == '\r') { - super.read(); // LF - break; - } else { - chunkLen += (char)i; - if(!isHexDigit((char)i) || chunkLen.length()>10) throw new HTTPException("encountered problem while reading chunk length: " + chunkLen); - } - } - length = Integer.parseInt(chunkLen.trim(), 16); - if (length == 0) chunkedDone = true; - } - - public int read(byte[] b, int off, int len) throws IOException { - boolean good = false; - try { - if (length == 0 && info.contentLength == -1) { - readChunk(); - if (chunkedDone) { good = true; return -1; } - } else { - if (length == 0) { good = true; return -1; } - } - if (len > length) len = length; - int ret = b == null ? (int)super.skip(len) : super.read(b, off, len); - if (ret >= 0) { - length -= ret; - good = true; - } - return ret; - } finally { - if (!good) logger.warn(HTTP.class,"HTTP content shorter than expected\n"+ - " remaining : "+length+"\n"+ - " chunked : "+(info.contentLength == -1)+"\n"); - - // REMARK - how can we reset, we never set the mark, and what - // good would it do anyway? - // if (!good) reset(); - } - } - - public void close() throws IOException { - if (info.contentLength == -1) { - while(!chunkedDone) { - if (length != 0) skip(length); - readChunk(); - } - skip(2); - } else { - int skip = length; - // available might be 0 is this is a HEAD request - // skipping was blocking before this check was instated - if(available()==0) skip = 0; - if (skip != 0) skip(skip); - } - if (releaseMe != null) releaseMe.release(); - } - - } - - private HTTPEntityInfo newEntityInfo(Hashtable h){ - int cl = h.get("content-length") == null ? -1 : Integer.parseInt(h.get("content-length").toString()); - String timestamp = (String) h.get("last-modified"); - if(timestamp==null) timestamp=""; - String contentType = (String) h.get("content-type"); - if(contentType==null) contentType = ""; - return new HTTPEntityInfo (cl, timestamp, contentType); - } - - private HTTPInputStream newHTTPInputStream(InputStream in, Hashtable h, Semaphore releaseMe) throws IOException{ - HTTPEntityInfo info = newEntityInfo(h); - lastInfo = info; - if ("gzip".equals(h.get("content-encoding"))) in = new java.util.zip.GZIPInputStream(in); - return new HTTPInputStream(in, releaseMe, info); - } - - void reset() { - firstRequest = true; - in = null; - sock = null; - } - - - // Misc Helpers /////////////////////////////////////////////////////////////////////////////////// - - /** reads a set of HTTP headers off of the input stream, returning null if the stream is already at its end */ - private Hashtable parseHeaders(InputStream in, Cookie.Jar cookies) throws IOException { - Hashtable ret = new Hashtable(); - - // we can't use a BufferedReader directly on the input stream, since it will buffer past the end of the headers - byte[] buf = new byte[4096]; - int buflen = 0; - while(true) { - int read = in.read(); - if (read == -1 && buflen == 0) return null; - if (read == -1) throw new HTTPException("stream closed while reading headers"); - buf[buflen++] = (byte)read; - if (buflen >= 4 && buf[buflen - 4] == '\r' && buf[buflen - 3] == '\n' && - buf[buflen - 2] == '\r' && buf[buflen - 1] == '\n') - break; - if (buflen >=2 && buf[buflen - 1] == '\n' && buf[buflen - 2] == '\n') - break; // nice for people using stdio - if (buflen == buf.length) { - byte[] newbuf = new byte[buf.length * 2]; - System.arraycopy(buf, 0, newbuf, 0, buflen); - buf = newbuf; - } - } - - BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, buflen))); - String s = br.readLine(); - if (!s.startsWith("HTTP/")) throw new HTTPException("Expected reply to start with \"HTTP/\", got: " + s); - ret.put("STATUSLINE", s.substring(s.indexOf(' ') + 1)); - ret.put("HTTP", s.substring(5, s.indexOf(' '))); - - while((s = br.readLine()) != null && s.length() > 0) { - String front = s.substring(0, s.indexOf(':')).toLowerCase(); - String back = s.substring(s.indexOf(':') + 1).trim(); - // ugly hack: we never replace a Digest-auth with a Basic-auth (proxy + www) - if (front.endsWith("-authenticate") && ret.get(front) != null && !back.equals("Digest")) continue; - if (front.equals("set-cookie")) cookies.setCookie(back, host); - ret.put(front, back); - } - return ret; - } - - private Hashtable parseAuthenticationChallenge(String s) { - Hashtable ret = new Hashtable(); - - s = s.trim(); - ret.put("AUTHTYPE", s.substring(0, s.indexOf(' '))); - s = s.substring(s.indexOf(' ')).trim(); - - while (s.length() > 0) { - String val = null; - String key = s.substring(0, s.indexOf('=')); - s = s.substring(s.indexOf('=') + 1); - if (s.charAt(0) == '\"') { - s = s.substring(1); - val = s.substring(0, s.indexOf('\"')); - s = s.substring(s.indexOf('\"') + 1); - } else { - val = s.indexOf(',') == -1 ? s : s.substring(0, s.indexOf(',')); - s = s.indexOf(',') == -1 ? "" : s.substring(s.indexOf(',') + 1); - } - if (s.length() > 0 && s.charAt(0) == ',') s = s.substring(1); - s = s.trim(); - ret.put(key, val); - } - return ret; - } - - private String H(String s) throws IOException { - byte[] b = s.getBytes("UTF8"); - MD5 md5 = new MD5(); - md5.update(b, 0, b.length); - byte[] out = new byte[md5.getDigestSize()]; - md5.doFinal(out, 0); - String ret = ""; - for(int i=0; i<out.length; i++) { - ret += "0123456789abcdef".charAt((out[i] & 0xf0) >> 4); - ret += "0123456789abcdef".charAt(out[i] & 0x0f); - } - return ret; - } - - - // Proxy /////////////////////////////////////////////////////////// - - /** encapsulates most of the proxy logic; some is shared in HTTP.java */ - public static class Proxy { - - public String httpProxyHost = null; ///< the HTTP Proxy host to use - public int httpProxyPort = -1; ///< the HTTP Proxy port to use - public String httpsProxyHost = null; ///< seperate proxy for HTTPS - public int httpsProxyPort = -1; - public String socksProxyHost = null; ///< the SOCKS Proxy Host to use - public int socksProxyPort = -1; ///< the SOCKS Proxy Port to use - public String[] excluded = new String[] { }; ///< hosts to be excluded from proxy use; wildcards permitted - - // ** temporarily disabled so HTTP does not depend on org.ibex.js ** - //public JS proxyAutoConfigFunction = null; ///< the PAC script - public Object proxyAutoConfigFunction = null; ///< the PAC script - - public static Proxy detectProxyViaManual() { - Proxy ret = new Proxy(); - /* - ret.httpProxyHost = Platform.getEnv("http_proxy"); - if (ret.httpProxyHost != null) { - if (ret.httpProxyHost.startsWith("http://")) ret.httpProxyHost = ret.httpProxyHost.substring(7); - if (ret.httpProxyHost.endsWith("/")) - ret.httpProxyHost = ret.httpProxyHost.substring(0, ret.httpProxyHost.length() - 1); - if (ret.httpProxyHost.indexOf(':') != -1) { - ret.httpProxyPort = Integer.parseInt(ret.httpProxyHost.substring(ret.httpProxyHost.indexOf(':') + 1)); - ret.httpProxyHost = ret.httpProxyHost.substring(0, ret.httpProxyHost.indexOf(':')); - } else { - ret.httpProxyPort = 80; - } - } - - ret.httpsProxyHost = Platform.getEnv("https_proxy"); - if (ret.httpsProxyHost != null) { - if (ret.httpsProxyHost.startsWith("https://")) ret.httpsProxyHost = ret.httpsProxyHost.substring(7); - if (ret.httpsProxyHost.endsWith("/")) - ret.httpsProxyHost = ret.httpsProxyHost.substring(0, ret.httpsProxyHost.length() - 1); - if (ret.httpsProxyHost.indexOf(':') != -1) { - ret.httpsProxyPort = Integer.parseInt(ret.httpsProxyHost.substring(ret.httpsProxyHost.indexOf(':') + 1)); - ret.httpsProxyHost = ret.httpsProxyHost.substring(0, ret.httpsProxyHost.indexOf(':')); - } else { - ret.httpsProxyPort = 80; - } - } - - ret.socksProxyHost = Platform.getEnv("socks_proxy"); - if (ret.socksProxyHost != null) { - if (ret.socksProxyHost.startsWith("socks://")) ret.socksProxyHost = ret.socksProxyHost.substring(7); - if (ret.socksProxyHost.endsWith("/")) - ret.socksProxyHost = ret.socksProxyHost.substring(0, ret.socksProxyHost.length() - 1); - if (ret.socksProxyHost.indexOf(':') != -1) { - ret.socksProxyPort = Integer.parseInt(ret.socksProxyHost.substring(ret.socksProxyHost.indexOf(':') + 1)); - ret.socksProxyHost = ret.socksProxyHost.substring(0, ret.socksProxyHost.indexOf(':')); - } else { - ret.socksProxyPort = 80; - } - } - - String noproxy = Platform.getEnv("no_proxy"); - if (noproxy != null) { - StringTokenizer st = new StringTokenizer(noproxy, ","); - ret.excluded = new String[st.countTokens()]; - for(int i=0; st.hasMoreTokens(); i++) ret.excluded[i] = st.nextToken(); - } - - if (ret.httpProxyHost == null && ret.socksProxyHost == null) return null; - */ - return ret; - } - - /* - public static JSScope proxyAutoConfigRootScope = new ProxyAutoConfigRootScope(); - public static JS getProxyAutoConfigFunction(String url) { - try { - BufferedReader br = new BufferedReader(new InputStreamReader(new HTTP(url, true).GET())); - String s = null; - String script = ""; - while((s = br.readLine()) != null) script += s + "\n"; - if (Log.on) Log.info(Proxy.class, "successfully retrieved WPAD PAC:"); - if (Log.on) Log.info(Proxy.class, script); - - // MS CARP hack - Vector carpHosts = new Vector(); - for(int i=0; i<script.length(); i++) - if (script.regionMatches(i, "new Node(", 0, 9)) { - String host = script.substring(i + 10, script.indexOf('\"', i + 11)); - if (Log.on) Log.info(Proxy.class, "Detected MS Proxy Server CARP Script, Host=" + host); - carpHosts.addElement(host); - } - if (carpHosts.size() > 0) { - script = "function FindProxyForURL(url, host) {\nreturn \""; - for(int i=0; i<carpHosts.size(); i++) - script += "PROXY " + carpHosts.elementAt(i) + "; "; - script += "\";\n}"; - if (Log.on) Log.info(Proxy.class, "DeCARPed PAC script:"); - if (Log.on) Log.info(Proxy.class, script); - } - - JS scr = JS.fromReader("PAC script at " + url, 0, new StringReader(script)); - JS.cloneWithNewParentScope(scr, proxyAutoConfigRootScope).call(null, null, null, null, 0); - return (JS)proxyAutoConfigRootScope.get("FindProxyForURL"); - } catch (Exception e) { - if (Log.on) { - Log.info(Platform.class, "WPAD detection failed due to:"); - if (e instanceof JSExn) { - try { - org.ibex.js.JSArray arr = new org.ibex.js.JSArray(); - arr.addElement(((JSExn)e).getObject()); - } catch (Exception e2) { - Log.info(Platform.class, e); - } - } - else Log.info(Platform.class, e); - } - return null; - } - } - */ - - // Authorization /////////////////////////////////////////////////////////////////////////////////// - - public static class Authorization { - - static public String authorization = null; - static public String authorization2 = null; - static public Semaphore waitingForUser = new Semaphore(); - - // FIXME: temporarily disabled so we can use HTTP outside the core - /* - public static synchronized void getPassword(final String realm, final String style, - final String proxyIP, String oldAuth) throws IOException { - - // this handles cases where multiple threads hit the proxy auth at the same time -- all but one will block on the - // synchronized keyword. If 'authorization' changed while the thread was blocked, it means that the user entered - // a password, so we should reattempt authorization. - - if (authorization != oldAuth) return; - if (Log.on) Log.info(Authorization.class, "displaying proxy authorization dialog"); - Scheduler.add(new Callable() { - public void run() throws IOException, JSExn { - Box b = new Box(); - Template t = null; - // FIXME - //Template.buildTemplate("org/ibex/builtin/proxy_authorization.ibex", Stream.getInputStream((JS)Main.builtin.get("org/ibex/builtin/proxy_authorization.ibex")), new Ibex(null)); - t.apply(b); - b.put("realm", realm); - b.put("proxyIP", proxyIP); - } - }); - - waitingForUser.block(); - if (Log.on) Log.info(Authorization.class, "got proxy authorization info; re-attempting connection"); - } - */ - } - - - // ProxyAutoConfigRootJSScope //////////////////////////////////////////////////////////////////// - /* - public static class ProxyAutoConfigRootScope extends JSScope.Global { - - public ProxyAutoConfigRootScope() { super(); } - - public Object get(Object name) throws JSExn { - // #switch(name) - case "isPlainHostName": return METHOD; - case "dnsDomainIs": return METHOD; - case "localHostOrDomainIs": return METHOD; - case "isResolvable": return METHOD; - case "isInNet": return METHOD; - case "dnsResolve": return METHOD; - case "myIpAddress": return METHOD; - case "dnsDomainLevels": return METHOD; - case "shExpMatch": return METHOD; - case "weekdayRange": return METHOD; - case "dateRange": return METHOD; - case "timeRange": return METHOD; - case "ProxyConfig": return ProxyConfig; - // #end - return super.get(name); - } - - private static final JS proxyConfigBindings = new JS(); - private static final JS ProxyConfig = new JS() { - public Object get(Object name) { - if (name.equals("bindings")) return proxyConfigBindings; - return null; - } - }; - - public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn { - // #switch(method) - case "isPlainHostName": return (a0.toString().indexOf('.') == -1) ? Boolean.TRUE : Boolean.FALSE; - case "dnsDomainIs": return (a0.toString().endsWith(a1.toString())) ? Boolean.TRUE : Boolean.FALSE; - case "localHostOrDomainIs": - return (a0.equals(a1) || (a0.toString().indexOf('.') == -1 && a1.toString().startsWith(a0.toString()))) ? T:F; - case "isResolvable": try { - return (InetAddress.getByName(a0.toString()) != null) ? Boolean.TRUE : Boolean.FALSE; - } catch (UnknownHostException e) { return F; } - case "isInNet": - if (nargs != 3) return Boolean.FALSE; - try { - byte[] host = InetAddress.getByName(a0.toString()).getAddress(); - byte[] net = InetAddress.getByName(a1.toString()).getAddress(); - byte[] mask = InetAddress.getByName(a2.toString()).getAddress(); - return ((host[0] & mask[0]) == net[0] && - (host[1] & mask[1]) == net[1] && - (host[2] & mask[2]) == net[2] && - (host[3] & mask[3]) == net[3]) ? - Boolean.TRUE : Boolean.FALSE; - } catch (Exception e) { - throw new JSExn("exception in isInNet(): " + e); - } - case "dnsResolve": - try { - return InetAddress.getByName(a0.toString()).getHostAddress(); - } catch (UnknownHostException e) { - return null; - } - case "myIpAddress": - try { - return InetAddress.getLocalHost().getHostAddress(); - } catch (UnknownHostException e) { - if (Log.on) Log.info(this, "strange... host does not know its own address"); - return null; - } - case "dnsDomainLevels": - String s = a0.toString(); - int i = 0; - while((i = s.indexOf('.', i)) != -1) i++; - return new Integer(i); - case "shExpMatch": - StringTokenizer st = new StringTokenizer(a1.toString(), "*", false); - String[] arr = new String[st.countTokens()]; - String s = a0.toString(); - for (int i=0; st.hasMoreTokens(); i++) arr[i] = st.nextToken(); - return match(arr, s, 0) ? Boolean.TRUE : Boolean.FALSE; - case "weekdayRange": - TimeZone tz = (nargs < 3 || a2 == null || !a2.equals("GMT")) ? - TimeZone.getTimeZone("UTC") : TimeZone.getDefault(); - Calendar c = new GregorianCalendar(); - c.setTimeZone(tz); - c.setTime(new java.util.Date()); - java.util.Date d = c.getTime(); - int day = d.getDay(); - String d1s = a0.toString().toUpperCase(); - int d1 = 0, d2 = 0; - for(int i=0; i<days.length; i++) if (days[i].equals(d1s)) d1 = i; - - if (nargs == 1) - return d1 == day ? Boolean.TRUE : Boolean.FALSE; - - String d2s = a1.toString().toUpperCase(); - for(int i=0; i<days.length; i++) if (days[i].equals(d2s)) d2 = i; - - return ((d1 <= d2 && day >= d1 && day <= d2) || (d1 > d2 && (day >= d1 || day <= d2))) ? T : F; - - case "dateRange": throw new JSExn("Ibex does not support dateRange() in PAC scripts"); - case "timeRange": throw new JSExn("Ibex does not support timeRange() in PAC scripts"); - // #end - return super.callMethod(method, a0, a1, a2, rest, nargs); - } - private static boolean match(String[] arr, String s, int index) { - if (index >= arr.length) return true; - for(int i=0; i<s.length(); i++) { - String s2 = s.substring(i); - if (s2.startsWith(arr[index]) && match(arr, s2.substring(arr[index].length()), index + 1)) return true; - } - return false; - } - public static String[] days = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; - } - */ - - /** - * An implementation of Microsoft's proprietary NTLM authentication protocol. This code was derived from Eric - * Glass's work, and is copyright as follows: - * - * Copyright (c) 2003 Eric Glass (eglass1 at comcast.net). - * - * Permission to use, copy, modify, and distribute this document for any purpose and without any fee is hereby - * granted, provided that the above copyright notice and this list of conditions appear in all copies. - * The most current version of this document may be obtained from http://davenport.sourceforge.net/ntlm.html . - */ - public static class NTLM { - - public static final byte[] type1 = new byte[] { 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00 }; - - /** - * Calculates the NTLM Response for the given challenge, using the - * specified password. - * - * @param password The user's password. - * @param challenge The Type 2 challenge from the server. - * - * @return The NTLM Response. - */ - public static byte[] getNTLMResponse(String password, byte[] challenge) - throws UnsupportedEncodingException { - byte[] ntlmHash = ntlmHash(password); - return lmResponse(ntlmHash, challenge); - } - - /** - * Calculates the LM Response for the given challenge, using the specified - * password. - * - * @param password The user's password. - * @param challenge The Type 2 challenge from the server. - * - * @return The LM Response. - */ - public static byte[] getLMResponse(String password, byte[] challenge) - { - byte[] lmHash = lmHash(password); - return lmResponse(lmHash, challenge); - } - - /** - * Calculates the NTLMv2 Response for the given challenge, using the - * specified authentication target, username, password, target information - * block, and client challenge. - * - * @param target The authentication target (i.e., domain). - * @param user The username. - * @param password The user's password. - * @param targetInformation The target information block from the Type 2 - * message. - * @param challenge The Type 2 challenge from the server. - * @param clientChallenge The random 8-byte client challenge. - * - * @return The NTLMv2 Response. - */ - public static byte[] getNTLMv2Response(String target, String user, - String password, byte[] targetInformation, byte[] challenge, - byte[] clientChallenge) throws UnsupportedEncodingException { - byte[] ntlmv2Hash = ntlmv2Hash(target, user, password); - byte[] blob = createBlob(targetInformation, clientChallenge); - return lmv2Response(ntlmv2Hash, blob, challenge); - } - - /** - * Calculates the LMv2 Response for the given challenge, using the - * specified authentication target, username, password, and client - * challenge. - * - * @param target The authentication target (i.e., domain). - * @param user The username. - * @param password The user's password. - * @param challenge The Type 2 challenge from the server. - * @param clientChallenge The random 8-byte client challenge. - * - * @return The LMv2 Response. - */ - public static byte[] getLMv2Response(String target, String user, - String password, byte[] challenge, byte[] clientChallenge) - throws UnsupportedEncodingException { - byte[] ntlmv2Hash = ntlmv2Hash(target, user, password); - return lmv2Response(ntlmv2Hash, clientChallenge, challenge); - } - - /** - * Calculates the NTLM2 Session Response for the given challenge, using the - * specified password and client challenge. - * - * @param password The user's password. - * @param challenge The Type 2 challenge from the server. - * @param clientChallenge The random 8-byte client challenge. - * - * @return The NTLM2 Session Response. This is placed in the NTLM - * response field of the Type 3 message; the LM response field contains - * the client challenge, null-padded to 24 bytes. - */ - public static byte[] getNTLM2SessionResponse(String password, - byte[] challenge, byte[] clientChallenge) throws UnsupportedEncodingException { - byte[] ntlmHash = ntlmHash(password); - MD5 md5 = new MD5(); - md5.update(challenge, 0, challenge.length); - md5.update(clientChallenge, 0, clientChallenge.length); - byte[] sessionHash = new byte[8]; - byte[] md5_out = new byte[md5.getDigestSize()]; - md5.doFinal(md5_out, 0); - System.arraycopy(md5_out, 0, sessionHash, 0, 8); - return lmResponse(ntlmHash, sessionHash); - } - - /** - * Creates the LM Hash of the user's password. - * - * @param password The password. - * - * @return The LM Hash of the given password, used in the calculation - * of the LM Response. - */ - private static byte[] lmHash(String password) { - /* - byte[] oemPassword = password.toUpperCase().getBytes("UTF8"); - int length = java.lang.Math.min(oemPassword.length, 14); - byte[] keyBytes = new byte[14]; - System.arraycopy(oemPassword, 0, keyBytes, 0, length); - Key lowKey = createDESKey(keyBytes, 0); - Key highKey = createDESKey(keyBytes, 7); - byte[] magicConstant = "KGS!@#$%".getBytes("UTF8"); - Cipher des = Cipher.getInstance("DES/ECB/NoPadding"); - des.init(Cipher.ENCRYPT_MODE, lowKey); - byte[] lowHash = des.doFinal(magicConstant); - des.init(Cipher.ENCRYPT_MODE, highKey); - byte[] highHash = des.doFinal(magicConstant); - byte[] lmHash = new byte[16]; - System.arraycopy(lowHash, 0, lmHash, 0, 8); - System.arraycopy(highHash, 0, lmHash, 8, 8); - return lmHash; - */ - return null; - } - - /** - * Creates the NTLM Hash of the user's password. - * - * @param password The password. - * - * @return The NTLM Hash of the given password, used in the calculation - * of the NTLM Response and the NTLMv2 and LMv2 Hashes. - */ - private static byte[] ntlmHash(String password) throws UnsupportedEncodingException { - // FIXME - /* - byte[] unicodePassword = password.getBytes("UnicodeLittleUnmarked"); - MD4 md4 = new MD4(); - md4.update(unicodePassword, 0, unicodePassword.length); - byte[] ret = new byte[md4.getDigestSize()]; - return ret; - */ - return null; - } - - /** - * Creates the NTLMv2 Hash of the user's password. - * - * @param target The authentication target (i.e., domain). - * @param user The username. - * @param password The password. - * - * @return The NTLMv2 Hash, used in the calculation of the NTLMv2 - * and LMv2 Responses. - */ - private static byte[] ntlmv2Hash(String target, String user, - String password) throws UnsupportedEncodingException { - byte[] ntlmHash = ntlmHash(password); - String identity = user.toUpperCase() + target.toUpperCase(); - return hmacMD5(identity.getBytes("UnicodeLittleUnmarked"), ntlmHash); - } - - /** - * Creates the LM Response from the given hash and Type 2 challenge. - * - * @param hash The LM or NTLM Hash. - * @param challenge The server challenge from the Type 2 message. - * - * @return The response (either LM or NTLM, depending on the provided - * hash). - */ - private static byte[] lmResponse(byte[] hash, byte[] challenge) - { - /* - byte[] keyBytes = new byte[21]; - System.arraycopy(hash, 0, keyBytes, 0, 16); - Key lowKey = createDESKey(keyBytes, 0); - Key middleKey = createDESKey(keyBytes, 7); - Key highKey = createDESKey(keyBytes, 14); - Cipher des = Cipher.getInstance("DES/ECB/NoPadding"); - des.init(Cipher.ENCRYPT_MODE, lowKey); - byte[] lowResponse = des.doFinal(challenge); - des.init(Cipher.ENCRYPT_MODE, middleKey); - byte[] middleResponse = des.doFinal(challenge); - des.init(Cipher.ENCRYPT_MODE, highKey); - byte[] highResponse = des.doFinal(challenge); - byte[] lmResponse = new byte[24]; - System.arraycopy(lowResponse, 0, lmResponse, 0, 8); - System.arraycopy(middleResponse, 0, lmResponse, 8, 8); - System.arraycopy(highResponse, 0, lmResponse, 16, 8); - return lmResponse; - */ - return null; - } - - /** - * Creates the LMv2 Response from the given hash, client data, and - * Type 2 challenge. - * - * @param hash The NTLMv2 Hash. - * @param clientData The client data (blob or client challenge). - * @param challenge The server challenge from the Type 2 message. - * - * @return The response (either NTLMv2 or LMv2, depending on the @@ Diff output truncated at 100000 characters. @@ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------------ _______________________________________________ Vexi-svn mailing list Vexi-svn@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/vexi-svn