1 /* 2 * Copyright (c) 2002, 2004, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.io.*; 25 import java.nio.*; 26 import java.nio.channels.*; 27 import java.net.*; 28 import sun.net.www.MessageHeader; 29 30 /** 31 * This class encapsulates a HTTP request received and a response to be 32 * generated in one transaction. It provides methods for examaining the 33 * request from the client, and for building and sending a reply. 34 */ 35 36 public class HttpTransaction { 37 38 String command; 39 URI requesturi; 40 HttpServer.Server server; 41 MessageHeader reqheaders, reqtrailers; 42 String reqbody; 43 byte[] rspbody; 44 MessageHeader rspheaders, rsptrailers; 45 SelectionKey key; 46 int rspbodylen; 47 boolean rspchunked; 48 49 HttpTransaction (HttpServer.Server server, String command, 50 URI requesturi, MessageHeader headers, 51 String body, MessageHeader trailers, SelectionKey key) { 52 this.command = command; 53 this.requesturi = requesturi; 54 this.reqheaders = headers; 55 this.reqbody = body; 56 this.reqtrailers = trailers; 57 this.key = key; 58 this.server = server; 59 } 60 61 /** 62 * Get the value of a request header whose name is specified by the 63 * String argument. 64 * 65 * @param key the name of the request header 66 * @return the value of the header or null if it does not exist 67 */ 68 public String getRequestHeader (String key) { 69 return reqheaders.findValue (key); 70 } 71 72 /** 73 * Get the value of a response header whose name is specified by the 74 * String argument. 75 * 76 * @param key the name of the response header 77 * @return the value of the header or null if it does not exist 78 */ 79 public String getResponseHeader (String key) { 80 return rspheaders.findValue (key); 81 } 82 83 /** 84 * Get the request URI 85 * 86 * @return the request URI 87 */ 88 public URI getRequestURI () { 89 return requesturi; 90 } 91 92 public String toString () { 93 StringBuffer buf = new StringBuffer(); 94 buf.append ("Request from: ").append (key.channel().toString()).append("\r\n"); 95 buf.append ("Command: ").append (command).append("\r\n"); 96 buf.append ("Request URI: ").append (requesturi).append("\r\n"); 97 buf.append ("Headers: ").append("\r\n"); 98 buf.append (reqheaders.toString()).append("\r\n"); 99 buf.append ("Body: ").append (reqbody).append("\r\n"); 100 buf.append ("---------Response-------\r\n"); 101 buf.append ("Headers: ").append("\r\n"); 102 if (rspheaders != null) { 103 buf.append (rspheaders.toString()).append("\r\n"); 104 } 105 String rbody = rspbody == null? "": new String (rspbody); 106 buf.append ("Body: ").append (rbody).append("\r\n"); 107 return new String (buf); 108 } 109 110 /** 111 * Get the value of a request trailer whose name is specified by 112 * the String argument. 113 * 114 * @param key the name of the request trailer 115 * @return the value of the trailer or null if it does not exist 116 */ 117 public String getRequestTrailer (String key) { 118 return reqtrailers.findValue (key); 119 } 120 121 /** 122 * Add a response header to the response. Multiple calls with the same 123 * key value result in multiple header lines with the same key identifier 124 * @param key the name of the request header to add 125 * @param val the value of the header 126 */ 127 public void addResponseHeader (String key, String val) { 128 if (rspheaders == null) 129 rspheaders = new MessageHeader (); 130 rspheaders.add (key, val); 131 } 132 133 /** 134 * Set a response header. Searches for first header with named key 135 * and replaces its value with val 136 * @param key the name of the request header to add 137 * @param val the value of the header 138 */ 139 public void setResponseHeader (String key, String val) { 140 if (rspheaders == null) 141 rspheaders = new MessageHeader (); 142 rspheaders.set (key, val); 143 } 144 145 /** 146 * Add a response trailer to the response. Multiple calls with the same 147 * key value result in multiple trailer lines with the same key identifier 148 * @param key the name of the request trailer to add 149 * @param val the value of the trailer 150 */ 151 public void addResponseTrailer (String key, String val) { 152 if (rsptrailers == null) 153 rsptrailers = new MessageHeader (); 154 rsptrailers.add (key, val); 155 } 156 157 /** 158 * Get the request method 159 * 160 * @return the request method 161 */ 162 public String getRequestMethod (){ 163 return command; 164 } 165 166 /** 167 * Perform an orderly close of the TCP connection associated with this 168 * request. This method guarantees that any response already sent will 169 * not be reset (by this end). The implementation does a shutdownOutput() 170 * of the TCP connection and for a period of time consumes and discards 171 * data received on the reading side of the connection. This happens 172 * in the background. After the period has expired the 173 * connection is completely closed. 174 */ 175 176 public void orderlyClose () { 177 try { 178 server.orderlyCloseChannel (key); 179 } catch (IOException e) { 180 System.out.println (e); 181 } 182 } 183 184 /** 185 * Do an immediate abortive close of the TCP connection associated 186 * with this request. 187 */ 188 public void abortiveClose () { 189 try { 190 server.abortiveCloseChannel(key); 191 } catch (IOException e) { 192 System.out.println (e); 193 } 194 } 195 196 /** 197 * Get the SocketChannel associated with this request 198 * 199 * @return the socket channel 200 */ 201 public SocketChannel channel() { 202 return (SocketChannel) key.channel(); 203 } 204 205 /** 206 * Get the request entity body associated with this request 207 * as a single String. 208 * 209 * @return the entity body in one String 210 */ 211 public String getRequestEntityBody (){ 212 return reqbody; 213 } 214 215 /** 216 * Set the entity response body with the given string 217 * The content length is set to the length of the string 218 * @param body the string to send in the response 219 */ 220 public void setResponseEntityBody (String body){ 221 rspbody = body.getBytes(); 222 rspbodylen = body.length(); 223 rspchunked = false; 224 addResponseHeader ("Content-length", Integer.toString (rspbodylen)); 225 } 226 /** 227 * Set the entity response body with the given byte[] 228 * The content length is set to the gven length 229 * @param body the string to send in the response 230 */ 231 public void setResponseEntityBody (byte[] body, int len){ 232 rspbody = body; 233 rspbodylen = len; 234 rspchunked = false; 235 addResponseHeader ("Content-length", Integer.toString (rspbodylen)); 236 } 237 238 239 /** 240 * Set the entity response body by reading the given inputstream 241 * 242 * @param is the inputstream from which to read the body 243 */ 244 public void setResponseEntityBody (InputStream is) throws IOException { 245 byte[] buf = new byte [2048]; 246 byte[] total = new byte [2048]; 247 int total_len = 2048; 248 int c, len=0; 249 while ((c=is.read (buf)) != -1) { 250 if (len+c > total_len) { 251 byte[] total1 = new byte [total_len * 2]; 252 System.arraycopy (total, 0, total1, 0, len); 253 total = total1; 254 total_len = total_len * 2; 255 } 256 System.arraycopy (buf, 0, total, len, c); 257 len += c; 258 } 259 setResponseEntityBody (total, len); 260 } 261 262 /* chunked */ 263 264 /** 265 * Set the entity response body with the given array of strings 266 * The content encoding is set to "chunked" and each array element 267 * is sent as one chunk. 268 * @param body the array of string chunks to send in the response 269 */ 270 public void setResponseEntityBody (String[] body) { 271 StringBuffer buf = new StringBuffer (); 272 int len = 0; 273 for (int i=0; i<body.length; i++) { 274 String chunklen = Integer.toHexString (body[i].length()); 275 len += body[i].length(); 276 buf.append (chunklen).append ("\r\n"); 277 buf.append (body[i]).append ("\r\n"); 278 } 279 buf.append ("0\r\n"); 280 rspbody = new String (buf).getBytes(); 281 rspbodylen = rspbody.length; 282 rspchunked = true; 283 addResponseHeader ("Transfer-encoding", "chunked"); 284 } 285 286 /** 287 * Send the response with the current set of response parameters 288 * but using the response code and string tag line as specified 289 * @param rCode the response code to send 290 * @param rTag the response string to send with the response code 291 */ 292 public void sendResponse (int rCode, String rTag) throws IOException { 293 OutputStream os = new HttpServer.NioOutputStream(channel()); 294 PrintStream ps = new PrintStream (os); 295 ps.print ("HTTP/1.1 " + rCode + " " + rTag + "\r\n"); 296 if (rspheaders != null) { 297 rspheaders.print (ps); 298 } else { 299 ps.print ("\r\n"); 300 } 301 ps.flush (); 302 if (rspbody != null) { 303 os.write (rspbody, 0, rspbodylen); 304 os.flush(); 305 } 306 if (rsptrailers != null) { 307 rsptrailers.print (ps); 308 } else if (rspchunked) { 309 ps.print ("\r\n"); 310 } 311 ps.flush(); 312 } 313 314 /* sends one byte less than intended */ 315 316 public void sendPartialResponse (int rCode, String rTag)throws IOException { 317 OutputStream os = new HttpServer.NioOutputStream(channel()); 318 PrintStream ps = new PrintStream (os); 319 ps.print ("HTTP/1.1 " + rCode + " " + rTag + "\r\n"); 320 ps.flush(); 321 if (rspbody != null) { 322 os.write (rspbody, 0, rspbodylen-1); 323 os.flush(); 324 } 325 if (rsptrailers != null) { 326 rsptrailers.print (ps); 327 } 328 ps.flush(); 329 } 330 }