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