1818import java .io .DataOutputStream ;
1919import java .io .IOException ;
2020import java .io .InputStream ;
21+ import java .io .OutputStream ;
2122import java .net .HttpURLConnection ;
2223import java .net .URL ;
2324import java .util .HashMap ;
3940 * 2006-2-11
4041 */
4142public final class HttpRequest {
43+ /*
44+ * @(#)Base64.java 1.4 03/01/23
45+ *
46+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
47+ * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
48+ */
49+
50+ /**
51+ * Static methods for translating Base64 encoded strings to byte arrays
52+ * and vice-versa.
53+ *
54+ * @author Josh Bloch
55+ * @version 1.4, 01/23/03
56+ * @see Preferences
57+ * @since 1.4
58+ */
59+ static class Base64 {
60+ /**
61+ * Translates the specified byte array into a Base64 string as per
62+ * Preferences.put(byte[]).
63+ */
64+ static String byteArrayToBase64 (byte [] a ) {
65+ int aLen = a .length ;
66+ int numFullGroups = aLen /3 ;
67+ int numBytesInPartialGroup = aLen - 3 *numFullGroups ;
68+ int resultLen = 4 *((aLen + 2 )/3 );
69+ StringBuffer result = new StringBuffer (resultLen );
70+ char [] intToAlpha = intToBase64 ;
71+
72+ // Translate all full groups from byte array elements to Base64
73+ int inCursor = 0 ;
74+ for (int i =0 ; i <numFullGroups ; i ++) {
75+ int byte0 = a [inCursor ++] & 0xff ;
76+ int byte1 = a [inCursor ++] & 0xff ;
77+ int byte2 = a [inCursor ++] & 0xff ;
78+ result .append (intToAlpha [byte0 >> 2 ]);
79+ result .append (intToAlpha [(byte0 << 4 )&0x3f | (byte1 >> 4 )]);
80+ result .append (intToAlpha [(byte1 << 2 )&0x3f | (byte2 >> 6 )]);
81+ result .append (intToAlpha [byte2 & 0x3f ]);
82+ }
83+
84+ // Translate partial group if present
85+ if (numBytesInPartialGroup != 0 ) {
86+ int byte0 = a [inCursor ++] & 0xff ;
87+ result .append (intToAlpha [byte0 >> 2 ]);
88+ if (numBytesInPartialGroup == 1 ) {
89+ result .append (intToAlpha [(byte0 << 4 ) & 0x3f ]);
90+ result .append ("==" );
91+ } else {
92+ // assert numBytesInPartialGroup == 2;
93+ int byte1 = a [inCursor ++] & 0xff ;
94+ result .append (intToAlpha [(byte0 << 4 )&0x3f | (byte1 >> 4 )]);
95+ result .append (intToAlpha [(byte1 << 2 )&0x3f ]);
96+ result .append ('=' );
97+ }
98+ }
99+ // assert inCursor == a.length;
100+ // assert result.length() == resultLen;
101+ return result .toString ();
102+ }
103+
104+ /**
105+ * This array is a lookup table that translates 6-bit positive integer
106+ * index values into their "Base64 Alphabet" equivalents as specified
107+ * in Table 1 of RFC 2045.
108+ */
109+ private static final char intToBase64 [] = {
110+ 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' , 'H' , 'I' , 'J' , 'K' , 'L' , 'M' ,
111+ 'N' , 'O' , 'P' , 'Q' , 'R' , 'S' , 'T' , 'U' , 'V' , 'W' , 'X' , 'Y' , 'Z' ,
112+ 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' ,
113+ 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x' , 'y' , 'z' ,
114+ '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , '+' , '/'
115+ };
116+ }
117+
42118 private int status ;
119+ private String statusText ;
43120 private int readyState ;
44121
45122 private String responseText ;
@@ -51,10 +128,17 @@ public final class HttpRequest {
51128 private HttpURLConnection connection ;
52129 private String url ;
53130 private String method ;
131+ private String user ;
132+ private String password ;
54133
55134 private Map headers = new HashMap ();
56135 private String content ;
57136
137+ private boolean toAbort = false ;
138+ private boolean isDisconnected = false ;
139+ private OutputStream activeOS ;
140+ private InputStream activeIS ;
141+
58142 /**
59143 * Return read state of XMLHttpRequest.
60144 * @return int ready state
@@ -79,28 +163,41 @@ public Document getResponseXML() {
79163 if (responseXML != null ) {
80164 return responseXML ;
81165 }
82- if (responseText != null && responseText .length () != 0 ) {
83- DocumentBuilderFactory dbf = DocumentBuilderFactory .newInstance ();
84- dbf .setNamespaceAware (true );
85- dbf .setAttribute ("http://xml.org/sax/features/namespaces" , Boolean .TRUE );
86- try {
87- DocumentBuilder db = dbf .newDocumentBuilder ();
88- ByteArrayInputStream biStream = new ByteArrayInputStream (responseText .getBytes ());
89- responseXML = db .parse (biStream );
90- } catch (Exception e ) {
91- e .printStackTrace ();
92- }
166+ String type = connection .getHeaderField ("Content-Type" );
167+ if (type != null && (type .indexOf ("/xml" ) != -1 || type .indexOf ("+xml" ) != -1 )) {
168+ if (responseText != null && responseText .length () != 0 ) {
169+ DocumentBuilderFactory dbf = DocumentBuilderFactory .newInstance ();
170+ dbf .setNamespaceAware (true );
171+ dbf .setAttribute ("http://xml.org/sax/features/namespaces" , Boolean .TRUE );
172+ try {
173+ DocumentBuilder db = dbf .newDocumentBuilder ();
174+ ByteArrayInputStream biStream = new ByteArrayInputStream (responseText .getBytes ());
175+ responseXML = db .parse (biStream );
176+ } catch (Exception e ) {
177+ e .printStackTrace ();
178+ }
179+ }
180+ return responseXML ;
181+ } else {
182+ return null ;
93183 }
94- return responseXML ;
95184 }
96185 /**
97186 * Return respose code.
98187 * @return int response code. For more information please read about
99188 * HTTP protocol.
100189 */
101- public int getResponseCode () {
190+ public int getStatus () {
102191 return status ;
103192 }
193+ /**
194+ * Return respose code related text.
195+ * @return int response code. For more information please read about
196+ * HTTP protocol.
197+ */
198+ public String getStatusText () {
199+ return statusText ;
200+ }
104201 /**
105202 * Register XMLHttpRequest callback.
106203 *
@@ -118,6 +215,29 @@ public void registerOnReadyStateChange(IXHRCallback onreadystatechange) {
118215 public void setRequestHeader (String key , String value ) {
119216 headers .put (key , value );
120217 }
218+ /**
219+ * Get all response heades.
220+ * @return String the all reponse header value.
221+ */
222+ public String getAllResponseHeaders () {
223+ StringBuffer buffer = new StringBuffer ();
224+ int i = 1 ;
225+ while (true ) {
226+ String key = connection .getHeaderFieldKey (i );
227+ if (key != null ) {
228+ String value = connection .getHeaderField (i );
229+ buffer .append (key );
230+ buffer .append (": " );
231+ buffer .append (value );
232+ buffer .append ("\r \n " );
233+ } else {
234+ break ;
235+ }
236+ i ++;
237+ }
238+ buffer .append ("\r \n " );
239+ return buffer .toString ();
240+ }
121241 /**
122242 * Get response header with given key.
123243 * @param key String header keyword. For more information please
@@ -135,7 +255,7 @@ public String getResponseHeader(String key) {
135255 * @param url String remote URL. Should always be absolute URL.
136256 */
137257 public void open (String method , String url ) {
138- open (method , url , false );
258+ open (method , url , false , null , null );
139259 }
140260 /**
141261 * Open connection for HTTP request with given method, URL and mode.
@@ -145,14 +265,42 @@ public void open(String method, String url) {
145265 * @param async boolean whether send request asynchronously or not.
146266 */
147267 public void open (String method , String url , boolean async ) {
268+ open (method , url , async , null , null );
269+ }
270+ /**
271+ * Open connection for HTTP request with given method, URL and mode.
272+ *
273+ * @param method String "POST" or "GET" usually.
274+ * @param url String remote URL. Should always be absolute URL.
275+ * @param async boolean whether send request asynchronously or not.
276+ * @param user String user name
277+ */
278+ public void open (String method , String url , boolean async , String user ) {
279+ open (method , url , async , user , null );
280+ }
281+ /**
282+ * Open connection for HTTP request with given method, URL and mode.
283+ *
284+ * @param method String "POST" or "GET" usually.
285+ * @param url String remote URL. Should always be absolute URL.
286+ * @param async boolean whether send request asynchronously or not.
287+ * @param user String user name
288+ * @param password String user password
289+ */
290+ public void open (String method , String url , boolean async , String user , String password ) {
148291 this .asynchronous = async ;
149292 this .method = method ;
150293 this .url = url ;
294+ this .user = user ;
295+ this .password = password ;
151296 responseText = null ;
152297 responseXML = null ;
153298 readyState = 1 ;
299+ status = 200 ; // default OK
300+ statusText = null ;
301+ toAbort = false ;
154302 if (onreadystatechange != null ) {
155- onreadystatechange .onLoading ();
303+ onreadystatechange .onOpen ();
156304 }
157305 }
158306
@@ -171,13 +319,47 @@ public void send(String str) {
171319 if (asynchronous ) {
172320 new Thread (new Runnable () {
173321 public void run () {
174- request ();
322+ if (!toAbort ) {
323+ request ();
324+ }
175325 }
176326 }).start ();
177327 } else {
178328 request ();
179329 }
180330 }
331+ /**
332+ * Abort the sending or receiving data process.
333+ */
334+ public void abort () {
335+ toAbort = true ;
336+ isDisconnected = false ;
337+ checkAbort ();
338+ }
339+ private boolean checkAbort () {
340+ if (!toAbort ) return false ;
341+ if (activeOS != null ) {
342+ try {
343+ activeOS .close ();
344+ } catch (IOException e ) {
345+ e .printStackTrace ();
346+ }
347+ activeOS = null ;
348+ }
349+ if (activeIS != null ) {
350+ try {
351+ activeIS .close ();
352+ } catch (IOException e ) {
353+ e .printStackTrace ();
354+ }
355+ activeIS = null ;
356+ }
357+ if (!isDisconnected && connection != null ) {
358+ connection .disconnect ();
359+ isDisconnected = true ;
360+ }
361+ return true ;
362+ }
181363 /*
182364 * Try setup the real connection and send the request over HTTP protocol.
183365 */
@@ -193,44 +375,59 @@ private void request() {
193375 connection .setRequestProperty ("Content-Type" ,
194376 "application/x-www-form-urlencoded" );
195377 }
378+ if (user != null ) {
379+ String auth = user + ":" + (password != null ? password : "" );
380+ String base64Auth = HttpRequest .Base64 .byteArrayToBase64 (auth .getBytes ());
381+ connection .setRequestProperty ("Authorization" , "Basic " + base64Auth );
382+ }
196383 for (Iterator iter = headers .keySet ().iterator (); iter .hasNext ();) {
197384 String key = (String ) iter .next ();
198385 connection .setRequestProperty (key , (String ) headers .get (key ));
199386 }
200387 connection .setUseCaches (false );
388+ if (checkAbort ()) return ; // not yet send out a byte
201389 if ("post" .equalsIgnoreCase (method )) {
202390 DataOutputStream dos = new DataOutputStream (connection .getOutputStream ());
391+ activeOS = dos ;
203392 if (content != null ) {
204393 dos .writeBytes (content );
205394 }
395+ if (checkAbort ()) return ; // do not flush anything and close the connection
206396 dos .flush ();
207397 dos .close ();
398+ activeOS = null ;
208399 }
400+ if (checkAbort ()) return ; // just disconnect without receiving anything
209401 InputStream is = connection .getInputStream ();
402+ activeIS = is ;
210403 ByteArrayOutputStream baos = new ByteArrayOutputStream ();
211404 byte [] buffer = new byte [1024 ];
212405 int read ;
213- while ((read = is .read (buffer )) != -1 ) {
406+ while (!toAbort && (read = is .read (buffer )) != -1 ) {
407+ if (checkAbort ()) return ; // stop receiving anything
214408 if (readyState < 2 ) {
215409 readyState = 2 ;
410+ status = connection .getResponseCode ();
411+ statusText = connection .getResponseMessage ();
216412 if (onreadystatechange != null ) {
217- onreadystatechange .onLoaded ();
413+ onreadystatechange .onSent ();
218414 }
219415 }
220416 baos .write (buffer , 0 , read );
221417 if (readyState != 3 ) {
222418 readyState = 3 ;
223419 if (onreadystatechange != null ) {
224- onreadystatechange .onInteractive ();
420+ onreadystatechange .onReceiving ();
225421 }
226422 }
227423 }
424+ if (checkAbort ()) return ; // stop receiving anything
228425 is .close ();
426+ activeIS = null ;
229427 responseText = baos .toString ();
230- status = connection .getResponseCode ();
231428 readyState = 4 ;
232429 if (onreadystatechange != null ) {
233- onreadystatechange .onComplete ();
430+ onreadystatechange .onLoaded ();
234431 }
235432 connection .disconnect ();
236433 readyState = 0 ;
@@ -240,10 +437,11 @@ private void request() {
240437 }
241438 */
242439 } catch (IOException e ) {
440+ if (checkAbort ()) return ; // exception caused by abort action
243441 e .printStackTrace ();
244442 readyState = 4 ;
245443 if (onreadystatechange != null ) {
246- onreadystatechange .onComplete ();
444+ onreadystatechange .onLoaded ();
247445 }
248446 connection = null ;
249447 readyState = 0 ;
0 commit comments