001: // Copyright (C) 1998-2001 by Jason Hunter <jhunter_AT_acm_DOT_org>.
002: // All rights reserved. Use of this class is limited.
003: // Please see the LICENSE for more information.
004:
005: package com.oreilly.servlet;
006:
007: import java.io.*;
008: import java.net.*;
009: import java.util.*;
010:
011: import com.knowgate.misc.Base64Encoder;
012:
013: /**
014: * A class to simplify HTTP applet-server communication. It abstracts
015: * the communication into messages, which can be either GET or POST.
016: * <p>
017: * It can be used like this:
018: * <blockquote><pre>
019: * URL url = new URL(getCodeBase(), "/servlet/ServletName");
020: *
021: * HttpMessage msg = new HttpMessage(url);
022: *
023: * // Parameters may optionally be set using java.util.Properties
024: * Properties props = new Properties();
025: * props.put("name", "value");
026: *
027: * // Headers, cookies, and authorization may be set as well
028: * msg.setHeader("Accept", "image/png"); // optional
029: * msg.setCookie("JSESSIONID", "9585155923883872"); // optional
030: * msg.setAuthorization("guest", "try2gueSS"); // optional
031: *
032: * InputStream in = msg.sendGetMessage(props);
033: * </pre></blockquote>
034: * <p>
035: * This class is loosely modeled after the ServletMessage class written
036: * by Rod McChesney of JavaSoft.
037: *
038: * @author <b>Jason Hunter</b>, Copyright © 1998
039: * @version 1.3, 2000/10/24, fixed headers NPE bug
040: * @version 1.2, 2000/10/15, changed uploaded object MIME type to
041: * application/x-java-serialized-object
042: * @version 1.1, 2000/06/11, added ability to set headers, cookies,
043: and authorization
044: * @version 1.0, 1998/09/18
045: */
046: public class HttpMessage {
047:
048: URL servlet = null;
049: Hashtable headers = null;
050:
051: /**
052: * Constructs a new HttpMessage that can be used to communicate with the
053: * servlet at the specified URL.
054: *
055: * @param servlet the server resource (typically a servlet) with which
056: * to communicate
057: */
058: public HttpMessage(URL servlet) {
059: this .servlet = servlet;
060: }
061:
062: /**
063: * Performs a GET request to the servlet, with no query string.
064: *
065: * @return an InputStream to read the response
066: * @exception IOException if an I/O error occurs
067: */
068: public InputStream sendGetMessage() throws IOException {
069: return sendGetMessage(null);
070: }
071:
072: /**
073: * Performs a GET request to the servlet, building
074: * a query string from the supplied properties list.
075: *
076: * @param args the properties list from which to build a query string
077: * @return an InputStream to read the response
078: * @exception IOException if an I/O error occurs
079: */
080: public InputStream sendGetMessage(Properties args)
081: throws IOException {
082: String argString = ""; // default
083:
084: if (args != null) {
085: argString = "?" + toEncodedString(args);
086: }
087: URL url = new URL(servlet.toExternalForm() + argString);
088:
089: // Turn off caching
090: URLConnection con = url.openConnection();
091: con.setUseCaches(false);
092:
093: // Send headers
094: sendHeaders(con);
095:
096: return con.getInputStream();
097: }
098:
099: /**
100: * Performs a POST request to the servlet, with no query string.
101: *
102: * @return an InputStream to read the response
103: * @exception IOException if an I/O error occurs
104: */
105: public InputStream sendPostMessage() throws IOException {
106: return sendPostMessage(null);
107: }
108:
109: /**
110: * Performs a POST request to the servlet, building
111: * post data from the supplied properties list.
112: *
113: * @param args the properties list from which to build the post data
114: * @return an InputStream to read the response
115: * @exception IOException if an I/O error occurs
116: */
117: public InputStream sendPostMessage(Properties args)
118: throws IOException {
119: String argString = ""; // default
120: if (args != null) {
121: argString = toEncodedString(args); // notice no "?"
122: }
123:
124: URLConnection con = servlet.openConnection();
125:
126: // Prepare for both input and output
127: con.setDoInput(true);
128: con.setDoOutput(true);
129:
130: // Turn off caching
131: con.setUseCaches(false);
132:
133: // Work around a Netscape bug
134: con.setRequestProperty("Content-Type",
135: "application/x-www-form-urlencoded");
136:
137: // Send headers
138: sendHeaders(con);
139:
140: // Write the arguments as post data
141: DataOutputStream out = new DataOutputStream(con
142: .getOutputStream());
143: out.writeBytes(argString);
144: out.flush();
145: out.close();
146:
147: return con.getInputStream();
148: }
149:
150: /**
151: * Performs a POST request to the servlet, uploading a serialized object.
152: * <p>
153: * The servlet can receive the object in its <tt>doPost()</tt> method
154: * like this:
155: * <pre>
156: * ObjectInputStream objin =
157: * new ObjectInputStream(req.getInputStream());
158: * Object obj = objin.readObject();
159: * </pre>
160: * The type of the uploaded object can be determined through introspection.
161: *
162: * @param obj the serializable object to upload
163: * @return an InputStream to read the response
164: * @exception IOException if an I/O error occurs
165: */
166: public InputStream sendPostMessage(Serializable obj)
167: throws IOException {
168: URLConnection con = servlet.openConnection();
169:
170: // Prepare for both input and output
171: con.setDoInput(true);
172: con.setDoOutput(true);
173:
174: // Turn off caching
175: con.setUseCaches(false);
176:
177: // Set the content type to be application/x-java-serialized-object
178: con.setRequestProperty("Content-Type",
179: "application/x-java-serialized-object");
180:
181: // Send headers
182: sendHeaders(con);
183:
184: // Write the serialized object as post data
185: ObjectOutputStream out = new ObjectOutputStream(con
186: .getOutputStream());
187: out.writeObject(obj);
188: out.flush();
189: out.close();
190:
191: return con.getInputStream();
192: }
193:
194: /**
195: * Sets a request header with the given name and value. The header
196: * persists across multiple requests. The caller is responsible for
197: * ensuring there are no illegal characters in the name and value.
198: *
199: * @param name the header name
200: * @param value the header value
201: */
202: public void setHeader(String name, String value) {
203: if (headers == null) {
204: headers = new Hashtable();
205: }
206: headers.put(name, value);
207: }
208:
209: // Send the contents of the headers hashtable to the server
210: private void sendHeaders(URLConnection con) {
211: if (headers != null) {
212: Enumeration headerenum = headers.keys();
213: while (headerenum.hasMoreElements()) {
214: String name = (String) headerenum.nextElement();
215: String value = (String) headers.get(name);
216: con.setRequestProperty(name, value);
217: }
218: }
219: }
220:
221: /**
222: * Sets a request cookie with the given name and value. The cookie
223: * persists across multiple requests. The caller is responsible for
224: * ensuring there are no illegal characters in the name and value.
225: *
226: * @param name the header name
227: * @param value the header value
228: */
229: public void setCookie(String name, String value) {
230: if (headers == null) {
231: headers = new Hashtable();
232: }
233: String existingCookies = (String) headers.get("Cookie");
234: if (existingCookies == null) {
235: setHeader("Cookie", name + "=" + value);
236: } else {
237: setHeader("Cookie", existingCookies + "; " + name + "="
238: + value);
239: }
240: }
241:
242: /**
243: * Sets the authorization information for the request (using BASIC
244: * authentication via the HTTP Authorization header). The authorization
245: * persists across multiple requests.
246: *
247: * @param name the user name
248: * @param name the user password
249: */
250: public void setAuthorization(String name, String password) {
251: String authorization = Base64Encoder.encode(name + ":"
252: + password);
253: setHeader("Authorization", "Basic " + authorization);
254: }
255:
256: /*
257: * Converts a properties list to a URL-encoded query string
258: */
259: private String toEncodedString(Properties args) {
260: StringBuffer buf = new StringBuffer();
261: Enumeration names = args.propertyNames();
262: while (names.hasMoreElements()) {
263: String name = (String) names.nextElement();
264: String value = args.getProperty(name);
265: buf.append(URLEncoder.encode(name) + "="
266: + URLEncoder.encode(value));
267: if (names.hasMoreElements())
268: buf.append("&");
269: }
270: return buf.toString();
271: }
272: }
|