[Webfunds-commits] java/webfunds/comms HttpSocketAgent.java

Ian Grigg iang@cypherpunks.ai
Tue, 20 Mar 2001 19:10:49 -0400 (AST)


iang        01/03/20 19:10:48

  Added:       webfunds/comms HttpSocketAgent.java
  Log:
  split out Socket code into its own CommsAgent, there was nothing in
  common anyway

Revision  Changes    Path
1.1                  java/webfunds/comms/HttpSocketAgent.java

Index: HttpSocketAgent.java
===================================================================
/*
 * $Id: HttpSocketAgent.java,v 1.1 2001/03/20 23:10:48 iang Exp $
 *
 * Copyright (c) 1995-2001 Systemics Inc on behalf of
 * the WebFunds Development Team.  All Rights Reserved.
 */
package webfunds.comms;

import java.io.PrintWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

import java.net.URL;

import webfunds.util.Panic;

/**
 * Instances of this class are used to perform Http requests.
 */
public class HttpSocketAgent
    extends CommsAgent
{
    /**
     * The HTTP location
     */
    protected URL url;

    transient protected PrintWriter pw;

    /**
     * Create a new HttpSocketAgent object.
     *
     * @param url the http location where requests are sent
     */
    public HttpSocketAgent(URL url, PrintWriter bug)
    {
        debug(bug, "             a= ");
        this.url = url;
    }

    /**
     * Send a Http request and await the reply.
     * @return the reply
     * @except AgentReplyException could not interpret reply
     * @except AgentURLException connect details bad, check them
     * @except AgentConnectException connection refused (try again later)
     */
    public byte[] request(byte[] request)
        throws AgentReplyException, AgentURLException,
               AgentConnectException, AgentLaterException
    {
        try
        {
            return requestViaSocket(request);
        }
        catch (IOException ex)
        {
            //
            // This is a code failure.  Stop.
            // Figure out what and why, catch it in the code,
            // and convert it to a meaningful exception.
            //
            String s = "caught bad IOEx: " + ex;
            logmsg(s);
            ex.printStackTrace(err());
            throw new Panic("Fatal: " + ex);
        }
    }

    /**
     * Send a Http request and await the reply.
     * This method uses the low level socket code to get more control.
     *
     * @return the reply
     * @except AgentReplyException could not interpret reply
     * @except AgentURLException connect details bad, check them
     * @except AgentConnectException connection refused (try again later)
     * @except IOException code error, catch and die
     */
    private byte[] requestViaSocket(byte[] request)
        throws AgentReplyException, AgentURLException,
               AgentConnectException, AgentLaterException,
               IOException  // failure!  catch and convert
    {

        String host = url.getHost();
        int    port = url.getPort();
        byte[] post = getPostData(request, host + ":" + port);

        logmsg("Opening Socket to: " + host + " : " + port + " (" + 0 + ")");
        byte[] out;
        try {
            out = RawHttp.getSocket(host, port, post, 0);
        } catch (RawURLException ex) {
            String s = "RawURLEx: " + ex;
            logmsg(s);
            throw new AgentURLException(s);
        } catch (RawConnectException ex) {
            String s = "RawConnectEx: " + ex;
            logmsg(s);
            throw new AgentConnectException(s);
        }

        byte[] reply = getPostReply(out);        // throws ReplyEx
        return reply ;

    }



    /**
     * @Return the next line
     * @except IOException some unknown failure (others inherit from IOEx)
     */
    protected String getLine(InputStream is)
        throws IOException
    {
        byte[] buf   = new byte[128];
        int    i     = 0;

        do
        {
             int ch = is.read();
             if (ch < 0)
             {
                 if (i == 0)                                   // EOF
                     return null;
                 throw new IOException("EOF after " + i + ":" +
                                       " <<" + new String(buf, 0, i) + ">>");
             }
             buf[i] = (byte)ch;
             if (i > 0 && buf[i] == '\n' && buf[i-1] == '\r')  // CRNL
                 return new String(buf, 0, i - 1);             // no term
             else if (buf[i] == '\n')                          // NL only?
                 return new String(buf, 0, i);                 // no term

        } while (++i < buf.length) ;

        throw new IOException("too long: <<" + new String(buf) + ">>");
    }



    protected static final String serverVersion = "HTTP/";
    protected static final String contentLength = "Content-length: ";

    /**
     * Return the message from a POST reply
     * @param buf the byte array with the reply in it
     *
     * @return the reply body
     * @except IOException some unknown failure (others inherit from IOEx)
     */
    protected byte[] getPostReply(byte[] buf)
        throws AgentReplyException, AgentLaterException
    {
        ByteArrayInputStream bais = new ByteArrayInputStream(buf);
        return getPostReply(bais);
    }

    /**
     * Return the message from a POST reply
     * @return the reply body
     * @except IOException some unknown failure (others inherit from IOEx)
     */
    protected byte[] getPostReply(InputStream is)
        throws AgentReplyException, AgentLaterException
    {
        boolean ok = true;
        int length = -1;
        String errors = "";
        boolean later = false;

        //
        // On the reads, the timeout will cause a
        // java.io.InterruptedIOException.
        //
        while (true)
        {
            String s;
            try {
                s = getLine(is);
            } catch (IOException ex) {
                logmsg(ex.getMessage());
                throw new AgentReplyException("headers: " + ex);
            }
            if (s == null)           // sometimes returns closed as EOF
            {
                ok = false;
                later = true;
                errors += "  later, down: <<EOF>>";
                break;
            }

            String line = s.trim();

            if ("".equals(line))     // this is empty line, waiting for
                break ;              // next is message body

            if (line.startsWith(serverVersion))
            {
                if (line.indexOf(" 500 Internal Error") >= 0)
                {
                    ok = false;
                    later = true;
                    errors += "  later, down: <<" + line + ">>";
                }
                else if (line.indexOf(" 200 ") < 0)
                {
                    ok = false;
                    errors += "  failed: <<" + line + ">>";
                }
            }
            else if (line.startsWith(contentLength))
            {
                int len = line.indexOf(": ");
                if (len < 0 || (len + 2) >= line.length())
                    ok = false;
                len += 2;
                String lenString = line.substring(len);
                try {
                    length = new Integer(lenString).intValue();
                } catch (NumberFormatException ex) {
                    ok = false;
                    errors += "  bad len: <<" + line + ">>";
                    logmsg(errors);
                }
            }
        }

        int avail;
        try {
            avail = is.available();
        } catch (IOException ex) {
            throw new Panic("is.available: " + ex);
        }

        if (errors.length() > 0)
        {
            String s = "HTTP failed (" + avail + " bytes) : " + errors + "\n";
            byte[] errorHtml = new byte[avail];
            int i;
            try {
                i = is.read(errorHtml);
            } catch (IOException ex) {
                throw new AgentReplyException("is.read: " + ex);
            }
            if (i > 0)
                s += "\n" + new String(errorHtml, 0, i) + "\n";
            logmsg(s);
            if (later)
                throw new AgentLaterException(s);
            else
                throw new AgentReplyException(s);
        }

        if (length <= 0)
        {
            String s = "no Content-length: " + errors + "\n";
            logmsg(s);
            throw new AgentReplyException("HTTP failed: " + s + "\n");
        }

        //
        //  All looks good.  Read in the data.
        //  Hopefully only one read required, but you never know.
        //
        byte[] buf;
        buf = new byte[length];
        int rr = 0;
        while (rr < length)
        {
            int i;
            try {
                i = is.read(buf, rr, length - rr);
            } catch (IOException ex) {
                throw new AgentReplyException("is.read ("+rr+"): " + ex);
            }
            if (i < 0)
                throw new AgentReplyException("premature EOF at " +
                                                 rr + " of " + length);
            rr += i;
        }

        return buf;
    }

    //
    //  Doesn't handle
    //           * proxy authentication
    //           * chunking
    //  Need to add these.
    //
    protected static final String end = "\r\n";
                                    // also. * is possible in place of empty /
    protected static final String cmd = "POST / HTTP/1.1" + end;
    protected static final String len = "Content-Length: ";
    protected static final String host = "Host: ";
    protected static final String con  = "Connection: close" + end;
    protected static final String type = "x-application/sox-2";
    protected static final String Type = "Content-Type: " + type + end;

    protected byte[] getPostData(byte[] request, String whereTo)
    {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        String Length = len + request.length + end;
        String Host = host + whereTo + end;


        try
        {
            baos.write(cmd.getBytes());
            baos.write(Host.getBytes());
            baos.write(Length.getBytes());
            baos.write(Type.getBytes());
            // baos.write(con.getBytes());
            baos.write(end.getBytes());       // extra line to separate body
            baos.write(request);
        }
        catch (IOException ex)
        {
            ex.printStackTrace();
            throw new Panic("getPostData: baos.write() " + ex);
        }

        byte[] buf = baos.toByteArray();
        try {
            baos.close();
        } catch (IOException ex) {
            ex.printStackTrace();
            logmsg("getPostData: baos.close() " + ex);
        }
        return buf ;
    }

    public String toString()
    {
        String s = "HttpSocketAgent:";
        s += "  Location: "+url;
        return s;
    }
}