001: /*
002: * HttpLoadProcess.java
003: *
004: * Copyright (C) 2000-2003 Peter Graves
005: * $Id: HttpLoadProcess.java,v 1.2 2003/06/28 01:20:47 piso Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.j;
023:
024: import java.io.InputStream;
025: import java.io.OutputStream;
026: import java.io.OutputStreamWriter;
027: import java.net.Socket;
028: import java.net.URL;
029: import java.security.Provider;
030: import java.security.Security;
031: import java.util.List;
032: import javax.swing.SwingUtilities;
033:
034: public final class HttpLoadProcess extends LoadProcess implements
035: BackgroundProcess, Runnable, Cancellable {
036: private Socket socket;
037: private boolean render = true;
038:
039: private String request;
040: private String responseHeaders;
041: private String contentType;
042:
043: private int redirectionCount;
044:
045: private FastStringBuffer sbHeaders = new FastStringBuffer();
046:
047: public HttpLoadProcess(Buffer buffer, HttpFile file) {
048: super (buffer, file);
049: }
050:
051: public final String getRequest() {
052: return request;
053: }
054:
055: public final String getResponseHeaders() {
056: return responseHeaders;
057: }
058:
059: public final String getContentType() {
060: return contentType;
061: }
062:
063: private final void setContentType(String s) {
064: contentType = s;
065: }
066:
067: public void run() {
068: if (buffer != null) {
069: buffer.setBusy(true);
070: buffer.setBackgroundProcess(this );
071: }
072: load();
073: if (buffer != null && buffer.getBackgroundProcess() == this ) {
074: Log.debug("calling setBackgroundProcess(null)");
075: buffer.setBackgroundProcess(null);
076: buffer.setBusy(false);
077: }
078: }
079:
080: private void load() {
081: boolean usingProxy = false;
082: if (file.getProtocol() == File.PROTOCOL_HTTPS) {
083: if (!findProvider()) {
084: error("No SSL provider found");
085: return;
086: }
087: }
088: cache = Utilities.getTempFile();
089: if (cache == null) {
090: Log.error("HttpLoadProcess.load cache is null");
091: return; // Report error!
092: }
093: Debug.assertTrue(socket == null);
094: String hostName = file.getHostName();
095: int port = file.getPort();
096: if (file.getProtocol() == File.PROTOCOL_HTTPS) {
097: socket = createSSLSocket(hostName, port);
098: if (socket == null) {
099: if (!cancelled)
100: error("Can't create SSL socket");
101: return;
102: }
103: } else {
104: String httpProxy = Editor.preferences().getStringProperty(
105: "httpProxy");
106: if (httpProxy != null) {
107: if (httpProxy.startsWith("http://"))
108: httpProxy = httpProxy.substring(7);
109: int index = httpProxy.indexOf(':');
110: if (index >= 0) {
111: try {
112: port = Integer.parseInt(httpProxy
113: .substring(index + 1));
114: hostName = httpProxy.substring(0, index);
115: usingProxy = true;
116: } catch (NumberFormatException e) {
117: Log.error(e);
118: }
119: }
120: }
121: connect(hostName, port);
122: }
123: if (cancelled) {
124: Log.debug("cancelled!!");
125: if (cancelRunnable != null)
126: SwingUtilities.invokeLater(cancelRunnable);
127: return;
128: }
129: if (socket == null) {
130: if (errorRunnable != null)
131: SwingUtilities.invokeLater(errorRunnable);
132: return;
133: }
134: String location = null;
135: boolean redirected = false;
136: String encoding = null;
137: try {
138: InputStream in = socket.getInputStream();
139: OutputStreamWriter writer = new OutputStreamWriter(socket
140: .getOutputStream());
141: FastStringBuffer sb = new FastStringBuffer(1024);
142: sb.append("GET ");
143: sb.append(usingProxy ? file.netPath() : file
144: .canonicalPath());
145: sb.append(" HTTP/1.0\r\n");
146: sb.append("Host: ");
147: sb.append(file.getHostName());
148: sb.append("\r\n");
149: String userAgent = Editor.preferences().getStringProperty(
150: Property.HTTP_USER_AGENT);
151: if (userAgent != null && userAgent.length() > 0) {
152: sb.append("User-Agent: ");
153: sb.append(userAgent);
154: sb.append("\r\n");
155: }
156: if (Editor.preferences().getBooleanProperty(
157: Property.HTTP_ENABLE_COOKIES)) {
158: String cookie = Cookie
159: .getCookie(new URL(file.netPath()));
160: if (cookie != null) {
161: sb.append("Cookie: ");
162: sb.append(cookie);
163: sb.append("\r\n");
164: }
165: }
166: sb.append("\r\n");
167: request = sb.toString();
168: writer.write(request);
169: writer.flush();
170: sb.setLength(0);
171: sbHeaders.append(request);
172: OutputStream out = cache.getOutputStream();
173: byte[] buf = new byte[16384];
174: long totalBytes = 0;
175: int totalLength = 0; // Includes length of response headers.
176: if (progressNotifier != null)
177: progressNotifier.progressStart();
178: while (!cancelled) {
179: int bytesRead = 0;
180: try {
181: // We may get an exception here if the user cancels.
182: bytesRead = in.read(buf);
183: } catch (Exception e) {
184: if (!cancelled)
185: Log.error(e);
186: }
187: if (bytesRead <= 0)
188: break;
189: if (sb != null) {
190: int oldLength = sb.length();
191: sb
192: .append(new String(buf, 0, bytesRead,
193: "ISO8859_1"));
194: String s = sb.toString();
195: int skip = 4; // "\r\n\r\n"
196: int index = s.indexOf("\r\n\r\n");
197: if (index < 0) {
198: index = s.indexOf("\n\n");
199: skip = 2; // "\n\n"
200: }
201: if (index >= 0) {
202: // We've got the headers.
203: sb = null;
204: responseHeaders = s.substring(0, index + skip);
205: sbHeaders.append(responseHeaders);
206: int statusCode = getStatusCode(responseHeaders);
207: Log.debug("statusCode = " + statusCode);
208: if (statusCode == 301 || statusCode == 302) {
209: // "Moved Permanently", "Moved Temporarily"
210: location = getLocation(responseHeaders);
211: redirected = true;
212: Log.debug("redirected to |" + location
213: + "|");
214: Log.debug(request);
215: Log.debug(responseHeaders);
216: }
217: // Remove status line.
218: int end = responseHeaders.indexOf('\n');
219: if (end >= 0)
220: responseHeaders = responseHeaders
221: .substring(end + 1);
222: int contentLength = getContentLength(responseHeaders);
223: if (contentLength != 0)
224: totalLength = responseHeaders.length()
225: + contentLength;
226: Log.debug("responseHeaders = |"
227: + responseHeaders + "|");
228: Headers headers = Headers
229: .parse(responseHeaders);
230: setContentType(headers
231: .getValue(Headers.CONTENT_TYPE));
232: Log.debug("content-type = |" + contentType
233: + "|");
234: String charset = Utilities
235: .getCharsetFromContentType(contentType);
236: Log.debug("charset = |" + charset + "|");
237: if (charset != null)
238: encoding = Utilities
239: .getEncodingFromCharset(charset);
240: Log.debug("encoding = |" + encoding + "|");
241: if (Editor.preferences().getBooleanProperty(
242: Property.HTTP_ENABLE_COOKIES)) {
243: String cookie = headers
244: .getValue(Headers.SET_COOKIE);
245: if (cookie != null)
246: Cookie
247: .setCookie(new URL(file
248: .netPath()), cookie);
249: }
250: int offset = index - oldLength + skip;
251: int length = bytesRead - offset;
252: out.write(buf, offset, length);
253: }
254: } else
255: out.write(buf, 0, bytesRead);
256: totalBytes += bytesRead;
257: if (progressNotifier != null)
258: progressNotifier.progress("Received ", totalBytes,
259: totalLength);
260: }
261: if (progressNotifier != null)
262: progressNotifier.progressStop();
263: out.close();
264: in.close();
265: socket.close();
266: socket = null;
267: } catch (Exception e) {
268: Log.error(e);
269: }
270: if (cancelled)
271: cache.delete();
272: if (!cache.isFile())
273: cache = null;
274: if (!cancelled && render && redirected && redirectionCount < 5) {
275: if (cache != null) {
276: if (cache.isFile())
277: cache.delete();
278: cache = null;
279: }
280: File parent = file.getParentFile();
281: if (parent != null) {
282: // Recurse.
283: file = HttpFile
284: .getHttpFile((HttpFile) parent, location);
285: ++redirectionCount;
286: load();
287: return;
288: }
289: }
290: if (cache != null) {
291: // Success!
292: final HttpFile httpFile = (HttpFile) file;
293: httpFile.setCache(cache);
294: httpFile.setHeaders(sbHeaders.toString());
295: httpFile.setContentType(contentType);
296: httpFile.setEncoding(encoding);
297: cache.setEncoding(encoding);
298: if (successRunnable != null)
299: SwingUtilities.invokeLater(successRunnable);
300: } else if (cancelled) {
301: if (cancelRunnable != null)
302: SwingUtilities.invokeLater(cancelRunnable);
303: } else if (errorRunnable != null)
304: SwingUtilities.invokeLater(errorRunnable);
305: }
306:
307: private void connect(final String hostName, final int port) {
308: Debug.assertTrue(socket == null);
309: Log.debug("Connecting to " + hostName + " on port " + port
310: + "...");
311: if (progressNotifier != null)
312: progressNotifier.setText("Connecting to " + hostName
313: + " on port " + port + "...");
314: SocketConnection sc = new SocketConnection(hostName, port,
315: 30000, 200, this );
316: socket = sc.connect();
317: if (socket != null) {
318: if (progressNotifier != null)
319: progressNotifier.setText("Connected to " + hostName);
320: } else
321: setErrorText(sc.getErrorText());
322: }
323:
324: private boolean findProvider() {
325: Provider provider = null;
326: try {
327: provider = (Provider) Class.forName(
328: "com.sun.net.ssl.internal.ssl.Provider")
329: .newInstance();
330: } catch (Exception e) {
331: }
332: if (provider != null) {
333: Security.addProvider(provider);
334: System.setProperty("java.protocol.handler.pkgs",
335: "com.sun.net.ssl.internal.www.protocol");
336: return true;
337: }
338: return false;
339: }
340:
341: private Socket createSSLSocket(String hostName, int port) {
342: try {
343: // SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
344: Class SSLSocketFactory = Class
345: .forName("javax.net.ssl.SSLSocketFactory");
346: java.lang.reflect.Method getDefault = SSLSocketFactory
347: .getMethod("getDefault", new Class[0]);
348: Object factory = getDefault.invoke(null, new Object[0]);
349:
350: // Socket socket = factory.createSocket(hostName, port);
351: Class[] parameterTypes = new Class[2];
352: parameterTypes[0] = String.class;
353: parameterTypes[1] = Integer.TYPE;
354: java.lang.reflect.Method createSocket = factory.getClass()
355: .getMethod("createSocket", parameterTypes);
356: Object[] args = new Object[2];
357: args[0] = hostName;
358: args[1] = new Integer(port);
359: Socket socket = (Socket) createSocket.invoke(factory, args);
360: return socket;
361: } catch (Throwable t) {
362: Log.error(t);
363: return null;
364: }
365: }
366:
367: private static int getStatusCode(String responseHeaders) {
368: int begin = responseHeaders.indexOf(' ') + 1;
369: if (begin == 0)
370: return -1;
371: int end = responseHeaders.indexOf(' ', begin);
372: if (end < 0)
373: return -1;
374: try {
375: return Utilities.parseInt(responseHeaders.substring(begin,
376: end));
377: } catch (NumberFormatException e) {
378: return -1;
379: }
380: }
381:
382: private static String getLocation(String responseHeaders) {
383: final String lookFor = "\nlocation:";
384: final int index = responseHeaders.toLowerCase()
385: .indexOf(lookFor);
386: if (index < 0)
387: return null;
388: final int begin = index + lookFor.length();
389: final int end = responseHeaders.indexOf('\n', begin);
390: if (end < 0)
391: return null;
392: String location = responseHeaders.substring(begin, end).trim();
393: if (location.startsWith("http:/")
394: && !location.startsWith("http://")) {
395: // Be permissive in what we accept.
396: location = "http://".concat(location.substring(6));
397: }
398: return location;
399: }
400:
401: private static int getContentLength(String responseHeaders) {
402: final String lookFor = "\r\ncontent-length:";
403: final int index = responseHeaders.toLowerCase()
404: .indexOf(lookFor);
405: if (index < 0)
406: return 0; // No content length header.
407: final String value = responseHeaders.substring(
408: index + lookFor.length()).trim();
409: try {
410: return Utilities.parseInt(value);
411: } catch (NumberFormatException e) {
412: return 0;
413: }
414: }
415:
416: private void error(String errorText) {
417: Log.error(errorText);
418: if (errorRunnable != null) {
419: errorRunnable.setMessage(errorText);
420: SwingUtilities.invokeLater(errorRunnable);
421: }
422: }
423:
424: public static void httpShowHeaders() {
425: final Editor editor = Editor.currentEditor();
426: final Buffer buffer = editor.getBuffer();
427: final File file = buffer.getFile();
428: if (file instanceof HttpFile) {
429: editor.setWaitCursor();
430: final String title = "httpShowHeaders ".concat(file
431: .netPath());
432: Buffer buf = null;
433: for (BufferIterator it = new BufferIterator(); it.hasNext();) {
434: Buffer b = it.nextBuffer();
435: if (b instanceof OutputBuffer
436: && b.getParentBuffer() == buffer) {
437: if (title.equals(b.getTitle())) {
438: buf = b;
439: break;
440: }
441: }
442: }
443: if (buf == null) {
444: buf = OutputBuffer.getOutputBuffer(((HttpFile) file)
445: .getHeaders());
446: buf.setParentBuffer(buffer);
447: buf.setTitle(title);
448: }
449: editor.makeNext(buf);
450: editor.activateInOtherWindow(buf);
451: editor.setDefaultCursor();
452: }
453: }
454: }
|