[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;
}
}