home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1995 November / PCWK1195.iso / inne / win95 / sieciowe / hotja32.lzh / hotjava / classsrc / net / www / http / httpclient.java < prev    next >
Text File  |  1995-08-11  |  12KB  |  436 lines

  1. /*
  2.  * @(#)HttpClient.java    1.40 95/05/15 Jonathan Payne
  3.  *
  4.  * Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
  5.  *
  6.  * Permission to use, copy, modify, and distribute this software
  7.  * and its documentation for NON-COMMERCIAL purposes and without
  8.  * fee is hereby granted provided that this copyright notice
  9.  * appears in all copies. Please refer to the file "copyright.html"
  10.  * for further important copyright and licensing information.
  11.  *
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  13.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  14.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  15.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  16.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  17.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  18.  */
  19.  
  20. package net.www.http;
  21.  
  22. import java.io.*;
  23. import net.*;
  24. import java.util.*;
  25. import net.www.html.URL;
  26. import net.Firewall;
  27. import net.www.html.MessageHeader;
  28.  
  29. /**
  30.  * @version 1.40 15 May 1995
  31.  * @author Jonathan Payne
  32.  */
  33. public final class HttpClient extends NetworkClient {
  34.     static final boolean    debug = false;
  35.  
  36.     static final String    userAgentString = "User-Agent: HotJava/1.0alpha3\r\n";
  37.     static final String acceptString
  38.     = "Accept: text/html; image/gif; *, q=.2; */*, q=.2\r\n";
  39.  
  40.     /** Default port number for http daemons - this is not
  41.         a registered service as far as I can tell. */
  42.     static final int    httpPortNumber = 80;
  43.  
  44.     public static boolean   useProxyForFirewall = true;
  45.     public static String    firewallProxyHost = "dudette.eng";
  46.     public static int        firewallProxyPort = 80;
  47.  
  48.     public static boolean   useProxyForCaching = false;
  49.     public static String    cachingProxyHost = "sunweb.ebay";
  50.     public static int        cachingProxyPort = 80;
  51.  
  52.     /* instance-specific proxy fields override the static fields if */
  53.     /* set. */
  54.     private String        proxy = null;
  55.     private int            proxyPort = -1;
  56.  
  57.     /* request essentially succeeded */
  58.     public static final int    OK = 200;
  59.     public static final int    CREATED = 201;
  60.     public static final int    ACCEPTED = 202;
  61.     public static final int    PARTIAL = 203;
  62.  
  63.     /* document redirection */
  64.     public static final int    MOVED = 301;
  65.     public static final int    FOUND = 302;
  66.     public static final int    METHOD = 303;
  67.  
  68.     /* errors */
  69.     public static final int    BAD = 400;
  70.     public static final int    UNAUTHORIZED = 401;
  71.     public static final int    PAYMENT_REQUIRED = 402;
  72.     public static final int    FORBIDDEN = 403;
  73.     public static final int    NOT_FOUND = 404;
  74.     public static final int    INTERNAL_ERROR = 500;
  75.     public static final int    NOT_IMPLEMENTED = 501;
  76.  
  77.     int            status;
  78.     MessageHeader    mimeHeader = null;
  79.     boolean        usingProxy = false;
  80.     String        host;
  81.     String        authentication = null;
  82.  
  83.     /** Url being fetched. */
  84.     URL            url;
  85.  
  86.     public synchronized void openServer(String host, int port) {
  87.     this.host = host;
  88.     String   s;
  89.  
  90.     ProgressData.pdata.register(url);
  91.     try {
  92.  
  93.         if (debug) {
  94.         System.out.println("openServer " + host + ":" + port);
  95.         }
  96.         if ((s = Firewall.verifyAccess(host, port)) != null) {
  97.         String msg = "Applet at " + s + " attempted illegal URL access: ";
  98.  
  99.         msg = msg + url.toExternalForm();
  100.         Firewall.securityError(msg);
  101.         return;
  102.         }
  103.         
  104.         if (url.protocol.equals("http")) {
  105.         if (useProxyForCaching) {
  106.             /* This would only fail if the specified host is
  107.                unknown to the proxy, e.g., a local host was
  108.                specified that isn't exported to the net which
  109.                the proxy is on.  In that case, we keep trying
  110.                just in case the host is known locally. */        
  111.             super.openServer(cachingProxyHost, cachingProxyPort);
  112.             usingProxy = true;
  113.             return;
  114.         }
  115.         try {
  116.             super.openServer(host, port);
  117.         } catch (UnknownHostException e) {
  118.             if (useProxyForFirewall) {
  119.             if (proxy == null) {
  120.                 super.openServer(firewallProxyHost,firewallProxyPort);
  121.             } else {
  122.                 super.openServer(proxy, proxyPort);
  123.             }
  124.             usingProxy = true;
  125.             } else {
  126.             throw e;
  127.             }
  128.         }
  129.         } else {
  130.         // we're opening some other kind of url, most likely an
  131.         // ftp url.
  132.         if (proxy == null) {
  133.             super.openServer(firewallProxyHost,firewallProxyPort);
  134.         } else {
  135.             super.openServer(proxy, proxyPort);
  136.         }
  137.         usingProxy = true;
  138.         }
  139.     } catch (Object e) {
  140.         ProgressData.pdata.unregister(url);
  141.         throw e;
  142.     }
  143.     }
  144.  
  145.     /** Parse the first line of the HTTP request.  It usually looks
  146.     something like: "HTTP/1.0 <number> comment\r\n". */
  147.  
  148.     protected void getRequestStatus(String response, String name) {
  149.     if (debug) {
  150.         System.out.println(response);
  151.     }
  152.     if (response.startsWith("HTTP/1.0 ")) {
  153.         status = Integer.parseInt(response.substring(9, 12));
  154.     } else {
  155.         status = -1;
  156.     }
  157.     switch (status) {
  158.     case UNAUTHORIZED:
  159.         throw new UnauthorizedHttpRequestException(url, this);
  160.  
  161.     case BAD:
  162.     case PAYMENT_REQUIRED:
  163.     case INTERNAL_ERROR:
  164.     case NOT_IMPLEMENTED:
  165.         throw new Exception("HTTP request failed: " + response);
  166.  
  167.     case FORBIDDEN:
  168.     case NOT_FOUND:
  169.         //System.out.println("Throwing file not found");
  170.         throw new FileNotFoundException(response + " -- " + name);
  171.     }
  172.     }
  173.  
  174.     protected String getURLFile(URL u) {
  175.     if (usingProxy) {
  176.         String result = u.protocol + "://" + u.host;
  177.         if (u.port != -1) {
  178.         result += ":" + u.port;
  179.         }
  180.         return result + u.file;
  181.     } else {
  182.         return u.file;
  183.     }
  184.     }
  185.  
  186.     protected void post(URL u) {
  187.     int port = u.port == -1 ? httpPortNumber : u.port;
  188.  
  189.     openServer(u.host, port);
  190.  
  191.     String cmd = "POST " + getURLFile(u) + " HTTP/1.0\r\n";
  192.     cmd += userAgentString;
  193.     cmd += "Referer: " + u.fromUrl.toExternalForm() + "\r\n";
  194.     cmd += acceptString;
  195.     if (authentication != null) {
  196.         cmd += getAuthentication();
  197.     }
  198.     cmd += "Content-type: application/x-www-form-urlencoded\r\n";
  199.     cmd += "Content-length: " + u.postData.length() + "\r\n\r\n";
  200.     serverOutput.print(cmd);
  201.     serverOutput.print(u.postData);
  202.     serverOutput.print("\r\n");
  203.     if (debug) {
  204.         System.out.println(cmd + u.postData);
  205.     }
  206.  
  207.     processRequest(u.file);
  208.     }
  209.  
  210.     protected void get(URL url) {
  211.     openServer(url.host, url.port == -1 ? httpPortNumber : url.port);
  212.  
  213.     String    cmd = "GET " + getURLFile(url)
  214.         + " HTTP/1.0\r\n" + userAgentString
  215.         + getAuthentication()
  216.         + acceptString
  217.         + "\r\n";
  218.     serverOutput.print(cmd);
  219.     serverOutput.flush();
  220.     if (debug) {
  221.         System.out.println(cmd);
  222.     }
  223.     processRequest(url.file);
  224.     }
  225.  
  226.     protected void processRequest(String name) {
  227.     /*
  228.      * Read until 200 bytes or EOF, or until we hit a blank
  229.      * line, whichever occurs first, looking for the string
  230.      * "HTTP/1.0" at the beginning of the line.  This is not
  231.      * strictly speaking necessary from the spec., but it is
  232.      * necessary since Netscape does it and therefore people
  233.      * have written their pages this way, broken as it is.  An
  234.      * example is mumble:80 which sometimes spews the line
  235.      * "Trying..." before returning a valid HTTP reply.
  236.      *
  237.      * In any case we always retain the first line of the
  238.      * response because it is useful if the server turns out to
  239.      * be a pre-HTTP1.0 server.
  240.      */
  241.  
  242.     BufferedInputStream bis = (BufferedInputStream) serverInput;
  243.     String status_response = null;
  244.     String firstLine = null;
  245.  
  246.     bis.mark(200);
  247.     DataInputStream dis = new DataInputStream(new TelnetInputStream(serverInput, false));
  248.  
  249.     while (true) {
  250.         String  line = dis.readLine();
  251.  
  252.         if (debug) {
  253.         System.err.println(line);
  254.         }
  255.  
  256.         if (firstLine == null) {
  257.         if (line == null) {
  258.             throw new SocketException("Unexpected EOF");
  259.         }
  260.         firstLine = new String(line);
  261.         }
  262.         if (line == null || line.length() == 0) /* end of header */
  263.         break;
  264.         if (line.startsWith("HTTP/1.0")) {
  265.         status_response = new String(line);
  266.         if (!firstLine.startsWith("HTTP/1.0")) {
  267.             System.err.println("Warning: The first line from "+url.toExternalForm()+" was not HTTP/1.0 compliant.");
  268.             System.err.println("         \""+firstLine+"\"");
  269.         }
  270.         break;
  271.         }
  272.     }
  273.  
  274.     bis.reset();
  275.  
  276.     try {
  277.         try {
  278.         if (status_response != null) {
  279.         byte buf[] = new byte[10];
  280.         int count = 0;
  281.         int c;
  282.  
  283.         /* Even if the request status indicates error, we parse
  284.            the mime header in case there is something in the mime
  285.            header we need in order to recover from the error. E.g.,
  286.            authentication. */
  287.         try {
  288.             getRequestStatus(status_response, name);
  289.         } finally {
  290.             mimeHeader = new MessageHeader(serverInput);
  291.         }
  292.         } else {
  293.         String    line = firstLine;
  294.  
  295.         if (line == null || line.length() == 0) {
  296.             throw new SocketException("Unexpected EOF");
  297.         } else if (line.indexOf("hostname unknown") != -1) {
  298.             throw new UnknownHostException(host);
  299.         } else if (line.indexOf("refused") != -1
  300.                || line.startsWith("</BODY>")) {
  301.             /* REMIND: </BODY> seems to be what the proxy on
  302.                sunweb.ebay spits back for connection refused,
  303.                but I am just hoping that *some* proxy server
  304.                indicates "connection refused" with the string
  305.                "refused" some place in the error message. */
  306.             throw new SocketException("Connection refused");
  307.         }
  308.         }
  309.         } finally {
  310.         ProgressData.pdata.connected(url);
  311.         }
  312.     } catch (UnauthorizedHttpRequestException e) {
  313.         /* don't close server, because we might let it
  314.            fall through to get whatever message is behind
  315.            the authentication, assuming the authentication
  316.            fails */
  317.         throw e;
  318.     } catch (Exception e) {
  319.         String cl = getHeaderField("content-length");
  320.         int len = 0;
  321.         if (cl == null || (len = Integer.parseInt(cl)) == 0) {
  322.         closeServer();
  323.         throw e;
  324.         }
  325.     }
  326.     }
  327.  
  328.     /** Close an open connection to the server. */
  329.     public void closeServer() {
  330.     if (url != null) {
  331.         ProgressData.pdata.unregister(url);
  332.         this.url = null;
  333.     }
  334.     super.closeServer();
  335.     }
  336.  
  337.     public String getHeaderField(String fieldName) {
  338.     return mimeHeader != null
  339.         ? mimeHeader.findValue(fieldName)
  340.         : null;
  341.     }
  342.  
  343.     public int getStatus() {
  344.     return status;
  345.     }
  346.  
  347.     public InputStream getInputStream() {
  348.     return serverInput;
  349.     }
  350.  
  351.     public void setAuthentication(String au) {
  352.     authentication = au;
  353.     }
  354.  
  355.     public String getAuthentication() {
  356.     if (authentication  == null) {
  357.         return "";
  358.     } else {
  359.         String  auth = "Authorization: " + authentication + "\r\n";
  360.         return auth;
  361.     }
  362.     }
  363.  
  364.     public String getAuthenticationScheme() {
  365.     return getAuthenticationField(1);
  366.     }
  367.  
  368.     public String getAuthenticationRealm() {
  369.     return getAuthenticationField(3);
  370.     }
  371.  
  372.     public String getAuthenticationField(int which) {
  373.     String    field = getHeaderField("www-authenticate");
  374.     if (field == null) {
  375.         return null;
  376.     }
  377.     StringTokenizer t = new StringTokenizer(field, " =\"");
  378.     while (--which > 0) {
  379.         t.nextToken();
  380.     }
  381.     if (t.hasMoreTokens()) {
  382.         return t.nextToken();
  383.     }
  384.     return null;
  385.     }
  386.  
  387.     protected void handleUrl(URL url) {
  388.     this.url = url;
  389.  
  390.     AuthenticationInfo  info = null;
  391.  
  392.     while (true) {
  393.         try {
  394.         if (url.isPostURL()) {
  395.             post(url);
  396.         } else {
  397.             get(url);
  398.         }
  399.         break;
  400.         } catch (UnauthorizedHttpRequestException e) {
  401.         if (info != null) {
  402.             AuthenticationInfo.uncacheInfo(info);
  403.             throw e;
  404.         }
  405.         info = AuthenticationInfo.getAuth(url,
  406.                           getAuthenticationRealm());
  407.         if (info == null) {
  408.             throw e;
  409.         }
  410.         setAuthentication(info.auth);
  411.         }
  412.     }
  413.     }
  414.  
  415.     public HttpClient(URL url, String auth, String proxy, int  proxyPort) {
  416.     this.proxy = proxy;
  417.     this.proxyPort = proxyPort;
  418.     setAuthentication(auth);
  419.     handleUrl(url);
  420.     }
  421.  
  422.     public HttpClient(URL url, String proxy, int  proxyPort) {
  423.     this.proxy = proxy;
  424.     this.proxyPort = proxyPort;
  425.     handleUrl(url);
  426.     }
  427.  
  428.     public HttpClient(URL url, String auth) {
  429.     this(url, auth, null, -1);
  430.     }
  431.  
  432.     public HttpClient(URL url) {
  433.     this(url, null, -1);
  434.     }
  435. }
  436.