blob: fa269f235bbf21368b5bbf2b9bad8459eb641358 [file] [log] [blame]
/*
* Copyright (c) 2002, 2004, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import sun.net.www.MessageHeader;
/**
* This class encapsulates a HTTP request received and a response to be
* generated in one transaction. It provides methods for examaining the
* request from the client, and for building and sending a reply.
*/
public class HttpTransaction {
String command;
URI requesturi;
HttpServer.Server server;
MessageHeader reqheaders, reqtrailers;
String reqbody;
byte[] rspbody;
MessageHeader rspheaders, rsptrailers;
SelectionKey key;
int rspbodylen;
boolean rspchunked;
HttpTransaction (HttpServer.Server server, String command,
URI requesturi, MessageHeader headers,
String body, MessageHeader trailers, SelectionKey key) {
this.command = command;
this.requesturi = requesturi;
this.reqheaders = headers;
this.reqbody = body;
this.reqtrailers = trailers;
this.key = key;
this.server = server;
}
/**
* Get the value of a request header whose name is specified by the
* String argument.
*
* @param key the name of the request header
* @return the value of the header or null if it does not exist
*/
public String getRequestHeader (String key) {
return reqheaders.findValue (key);
}
/**
* Get the value of a response header whose name is specified by the
* String argument.
*
* @param key the name of the response header
* @return the value of the header or null if it does not exist
*/
public String getResponseHeader (String key) {
return rspheaders.findValue (key);
}
/**
* Get the request URI
*
* @return the request URI
*/
public URI getRequestURI () {
return requesturi;
}
public String toString () {
StringBuffer buf = new StringBuffer();
buf.append ("Request from: ").append (key.channel().toString()).append("\r\n");
buf.append ("Command: ").append (command).append("\r\n");
buf.append ("Request URI: ").append (requesturi).append("\r\n");
buf.append ("Headers: ").append("\r\n");
buf.append (reqheaders.toString()).append("\r\n");
buf.append ("Body: ").append (reqbody).append("\r\n");
buf.append ("---------Response-------\r\n");
buf.append ("Headers: ").append("\r\n");
if (rspheaders != null) {
buf.append (rspheaders.toString()).append("\r\n");
}
String rbody = rspbody == null? "": new String (rspbody);
buf.append ("Body: ").append (rbody).append("\r\n");
return new String (buf);
}
/**
* Get the value of a request trailer whose name is specified by
* the String argument.
*
* @param key the name of the request trailer
* @return the value of the trailer or null if it does not exist
*/
public String getRequestTrailer (String key) {
return reqtrailers.findValue (key);
}
/**
* Add a response header to the response. Multiple calls with the same
* key value result in multiple header lines with the same key identifier
* @param key the name of the request header to add
* @param val the value of the header
*/
public void addResponseHeader (String key, String val) {
if (rspheaders == null)
rspheaders = new MessageHeader ();
rspheaders.add (key, val);
}
/**
* Set a response header. Searches for first header with named key
* and replaces its value with val
* @param key the name of the request header to add
* @param val the value of the header
*/
public void setResponseHeader (String key, String val) {
if (rspheaders == null)
rspheaders = new MessageHeader ();
rspheaders.set (key, val);
}
/**
* Add a response trailer to the response. Multiple calls with the same
* key value result in multiple trailer lines with the same key identifier
* @param key the name of the request trailer to add
* @param val the value of the trailer
*/
public void addResponseTrailer (String key, String val) {
if (rsptrailers == null)
rsptrailers = new MessageHeader ();
rsptrailers.add (key, val);
}
/**
* Get the request method
*
* @return the request method
*/
public String getRequestMethod (){
return command;
}
/**
* Perform an orderly close of the TCP connection associated with this
* request. This method guarantees that any response already sent will
* not be reset (by this end). The implementation does a shutdownOutput()
* of the TCP connection and for a period of time consumes and discards
* data received on the reading side of the connection. This happens
* in the background. After the period has expired the
* connection is completely closed.
*/
public void orderlyClose () {
try {
server.orderlyCloseChannel (key);
} catch (IOException e) {
System.out.println (e);
}
}
/**
* Do an immediate abortive close of the TCP connection associated
* with this request.
*/
public void abortiveClose () {
try {
server.abortiveCloseChannel(key);
} catch (IOException e) {
System.out.println (e);
}
}
/**
* Get the SocketChannel associated with this request
*
* @return the socket channel
*/
public SocketChannel channel() {
return (SocketChannel) key.channel();
}
/**
* Get the request entity body associated with this request
* as a single String.
*
* @return the entity body in one String
*/
public String getRequestEntityBody (){
return reqbody;
}
/**
* Set the entity response body with the given string
* The content length is set to the length of the string
* @param body the string to send in the response
*/
public void setResponseEntityBody (String body){
rspbody = body.getBytes();
rspbodylen = body.length();
rspchunked = false;
addResponseHeader ("Content-length", Integer.toString (rspbodylen));
}
/**
* Set the entity response body with the given byte[]
* The content length is set to the gven length
* @param body the string to send in the response
*/
public void setResponseEntityBody (byte[] body, int len){
rspbody = body;
rspbodylen = len;
rspchunked = false;
addResponseHeader ("Content-length", Integer.toString (rspbodylen));
}
/**
* Set the entity response body by reading the given inputstream
*
* @param is the inputstream from which to read the body
*/
public void setResponseEntityBody (InputStream is) throws IOException {
byte[] buf = new byte [2048];
byte[] total = new byte [2048];
int total_len = 2048;
int c, len=0;
while ((c=is.read (buf)) != -1) {
if (len+c > total_len) {
byte[] total1 = new byte [total_len * 2];
System.arraycopy (total, 0, total1, 0, len);
total = total1;
total_len = total_len * 2;
}
System.arraycopy (buf, 0, total, len, c);
len += c;
}
setResponseEntityBody (total, len);
}
/* chunked */
/**
* Set the entity response body with the given array of strings
* The content encoding is set to "chunked" and each array element
* is sent as one chunk.
* @param body the array of string chunks to send in the response
*/
public void setResponseEntityBody (String[] body) {
StringBuffer buf = new StringBuffer ();
int len = 0;
for (int i=0; i<body.length; i++) {
String chunklen = Integer.toHexString (body[i].length());
len += body[i].length();
buf.append (chunklen).append ("\r\n");
buf.append (body[i]).append ("\r\n");
}
buf.append ("0\r\n");
rspbody = new String (buf).getBytes();
rspbodylen = rspbody.length;
rspchunked = true;
addResponseHeader ("Transfer-encoding", "chunked");
}
/**
* Send the response with the current set of response parameters
* but using the response code and string tag line as specified
* @param rCode the response code to send
* @param rTag the response string to send with the response code
*/
public void sendResponse (int rCode, String rTag) throws IOException {
OutputStream os = new HttpServer.NioOutputStream(channel());
PrintStream ps = new PrintStream (os);
ps.print ("HTTP/1.1 " + rCode + " " + rTag + "\r\n");
if (rspheaders != null) {
rspheaders.print (ps);
} else {
ps.print ("\r\n");
}
ps.flush ();
if (rspbody != null) {
os.write (rspbody, 0, rspbodylen);
os.flush();
}
if (rsptrailers != null) {
rsptrailers.print (ps);
} else if (rspchunked) {
ps.print ("\r\n");
}
ps.flush();
}
/* sends one byte less than intended */
public void sendPartialResponse (int rCode, String rTag)throws IOException {
OutputStream os = new HttpServer.NioOutputStream(channel());
PrintStream ps = new PrintStream (os);
ps.print ("HTTP/1.1 " + rCode + " " + rTag + "\r\n");
ps.flush();
if (rspbody != null) {
os.write (rspbody, 0, rspbodylen-1);
os.flush();
}
if (rsptrailers != null) {
rsptrailers.print (ps);
}
ps.flush();
}
}