/*
Copyright (c) 2003-2008 ITerative Consulting Pty Ltd. All Rights Reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:
o Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
o Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the distribution.
o This jcTOOL Helper Class software, whether in binary or source form may not be used within,
or to derive, any other product without the specific prior written permission of the copyright holder
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package HTTPSupport;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Map.Entry;
import javax.servlet.http.HttpServletRequestWrapper;
import org.springframework.util.StringUtils;
import Framework.Array_Of_TextData;
import Framework.BinaryData;
import Framework.ErrorMgr;
import Framework.RemoteAccessException;
import Framework.Task;
import Framework.TextData;
import Framework.UsageException;
/**
* The HTTPBaseRequest class extends the {@link HTTPBaseMessage} class and provides all the methods necessary to build and send an HTTP request. A client can call these methods by sending an HTTP message to a server. A server can call these methods when receiving an HTTP request from a client.
*
*/
@SuppressWarnings("serial")
public class HTTPBaseRequest extends HTTPBaseMessage implements GenericRequest {
protected HttpServletRequestWrapper wrapper;
private String URLstring = "";
private String queryString = null;
private String serverName = "localhost";
private String serverPort = "80";
private URL url = null;
private String uri = "/"; // In lieu of anything else
private Proxy proxy; //PM:6/5/08 GMA-23
public HTTPBaseRequest() {
super();
this.method = "GET"; // On GenericMessage superclass...
}
private Socket socket;
public HTTPBaseRequest(/* InputStream input */Socket socket) {
this();
// this.input = input;
this.socket = socket;
// Ivan: immediately parse
parse();
}
public String getURL() {
return URLstring;
}
public void setURL(String url) {
// AD:23/7/2008 Check if the url contains a query string
int q = url.lastIndexOf('?');
if (q != -1) {
queryString = url.substring(q+1);
URLstring = url.substring(0, q);
} else {
URLstring = url;
}
if (StringUtils.hasText(queryString)) {
url = URLstring + "?" + queryString;
}
try {
this.url = new URL(url);
} catch (MalformedURLException e) {
UsageException errorVar = new UsageException("Malformed URL", e);
ErrorMgr.addError(errorVar);
throw errorVar;
}
}
/**
* The Send (Current URL) method sends the request to the current URL. You
* must first set the URL using SetURL.
*
* @param signal
* The signal parameter specifies the object receiver
* implementing the MessageReceiver interface. If the signal
* parameter is specified, the request send notification is
* asynchronous; otherwise the send notification is synchronous.
* @return response - If you use the signal parameter, the call immediately
* returns a NIL value. The response is received later by callback
* on the signal object.
*
* @author AD:18/7/2008 Partially implemented. Ignores Initialize method
*/
public HTTPBaseResponse send(MessageReceiver signal) {
if (signal == null) {
return send();
} else {
final MessageReceiver finalSignal = signal;
final HTTPBaseRequest finalRequest = this;
Thread t = new Task("HTTPBaseRequest.send") {
public void run() {
HTTPBaseResponse response = send();
finalSignal.process(finalRequest, response);
finalSignal.terminate();
}
};
t.start();
return null;
}
}
public HTTPBaseResponse send() {
try {
BufferedWriter out = null;
if (conn == null)
if (this.proxy != null){
conn = (HttpURLConnection) url.openConnection(this.proxy); //PM:6/5/08 GMA-23
} else {
conn = (HttpURLConnection) url.openConnection();
}
conn.setDoInput(true);
if (this.getMethod().equals("POST")) {
conn.setDoOutput(true);
conn.setRequestMethod(this.getMethod());
// AD:24/06/08 Use constant instead of hard coded string that was incorrect case
// AD:24/06/08 Use method to get ContentType. the protected attribute is never valid
conn.setRequestProperty(Constants.HTTP_HEADER_CONTENT_TYPE, this.getContentType());
Array_Of_TextData<TextData> headers = this.getHeaderNames();
String header;
String value;
for (int i = 0, sz = headers.size(); i < sz; i++) {
header = ((TextData) headers.get(i)).toString();
value = this.getHeader(header);
conn.setRequestProperty(header, value);
}
// AD:24/06/08 Use constant instead of hard coded string that was incorrect case
conn.setRequestProperty(Constants.HTTP_HEADER_CONTENT_LENGTH, String.valueOf(this
.getContentLength()));
conn.connect();
OutputStream os = conn.getOutputStream();
out = new BufferedWriter(new OutputStreamWriter(os));
// This breaks XML transmissions. CraigM 08/08/2007.
// out.newLine(); // Necessary to delimit the end of the headers.
TextData content = new TextData();
this.getBody().readText(content);
out.write(content.toString());
out.newLine();
out.flush();
out.close();
} else {
conn.setRequestMethod(this.getMethod());
// AD:24/06/08 Use constant instead of hard coded string that was incorrect case
// AD:24/06/08 Use method to get ContentType. the protected attribute is never valid
conn.setRequestProperty(Constants.HTTP_HEADER_CONTENT_TYPE, this.getContentType());
conn.connect();
}
HTTPBaseResponse br = new HTTPBaseResponse();
br.setContentType(conn.getContentType());
Set<Entry<String, List<String>>> entrySet = conn.getHeaderFields().entrySet();
Entry<String, List<String>> tmpEntry;
Iterator<Entry<String, List<String>>> itr = entrySet.iterator();
while (itr.hasNext()) {
tmpEntry = itr.next();
List<String> listOfValues = tmpEntry.getValue();
Iterator<String> lItr = listOfValues.iterator();
Object obj;
while (lItr.hasNext()) {
obj = lItr.next();
if (obj != null) {
if ((String) tmpEntry.getKey() != null) {
br.setHeader((String) tmpEntry.getKey(),
(String) obj);
}
}
}
}
Entity ent = new Entity();
// BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
// StringBuilder buff = new StringBuilder();
// String line;
// String separator = (String) System.getProperty("line.separator");
// while ((line = rd.readLine()) != null) {
// buff.append(line).append(separator);
// }
// ent.writeText(buff.toString());
// TF:02/07/2008:Changed this over to use binary information, otherwise we lose a lot of
// data when dealing with MIME UTF-8 messages
byte[] buffer = new byte[1024];
BufferedInputStream bis = new BufferedInputStream(conn.getInputStream());
int bytesRead;
int totalLength = 0;
while ((bytesRead = bis.read(buffer)) >= 0) {
BinaryData data = new BinaryData(buffer);
ent.writeBinary(data, bytesRead);
totalLength += bytesRead;
}
br.setBody(ent);
br.Status = conn.getResponseCode();
br.StatusText = conn.getResponseMessage();
// AD:24/06/08 Set the content type on the body to the same as the header
br.body.setContentType(conn.getContentType());
// Set the content length
br.body.setIntHeader(Constants.HTTP_HEADER_CONTENT_LENGTH, totalLength);
return br;
} catch (IOException e) {
RemoteAccessException errorVar = new RemoteAccessException("Cannot connect to URL: " + URLstring, e);
ErrorMgr.addError(errorVar);
throw errorVar;
}
}
public String getRequestURI() {
return uri;
}
public void parse() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
socket.getInputStream() /* input */));
StringBuilder buffer = new StringBuilder();
// Ivan: let the socket timeout after 0,5 sec, so the processing will continue if
// trying to read past the end of the stream
socket.setSoTimeout(500);
try {
int prevNewLine = -1;
int contentLength = -1;
int currentBodyLength = -1;
boolean endOfStream = false;
while (!endOfStream) {
char c = (char) reader.read();
if (c == -1)
break;
buffer.append(c);
// Ivan: determine Content-length header value
if (c == '\n' && prevNewLine != -1 && contentLength == -1) {
String headerLine = buffer.substring(prevNewLine);
String[] header = headerLine.split(":");
if ("Content-length".equalsIgnoreCase(header[0].trim())) {
contentLength = Integer.parseInt(header[1].trim());
}
}
if (c == '\n') {
// Ivan: look for empty line, marking end of HTTP headers and start of POST body
if (currentBodyLength == -1 && prevNewLine > -1) {
String headerLine = buffer.substring(prevNewLine);
if (headerLine.trim().length() == 0){
currentBodyLength = 0; // arrived in HTTP Body
}
}
prevNewLine = buffer.length();
}
if (currentBodyLength >=0 ) {
currentBodyLength++;
// AD:10/11/2008: If the currentBodyLength is zero then it is the start of the body which has the new line.
// Modified to avoid the empty line in the body
if(currentBodyLength == 0){
continue;
}
this.body.writeText(Character.toString(c)); // Ivan: save the body content
if (currentBodyLength-1 >= contentLength) {
endOfStream = true; // Ivan: stop reading from stream after <contentLength> bytes, to avoid the socket from blocking
}
}
}
} catch (SocketTimeoutException expected) {
}
String[] lines = buffer.toString().split("\n");
String[] requestLineElements = lines[0].split(" ");
// InputStreamReader reader = new InputStreamReader(input);
// BufferedReader in = new BufferedReader( reader);
// String requestLine = in.readLine();
// String[] requestLineElements = requestLine.split(" ");
this.setMethod(requestLineElements[0]);
this.uri = requestLineElements[1];
// boolean insidePostParameters = false;
//requestLine = in.readLine();
// while (in.ready()) {
// requestLine = in.readLine();
// if (insidePostParameters) {
// this.Body.writeLine(requestLine);
// }
// else if (requestLine.trim().length() == 0) {
// // POST parameters are separated from the headers by a blank line
// insidePostParameters = true;
// }
//
//// requestLine = in.readLine();
// }
// for (int i = 0; i < lines.length; i++) {
// String requestLine = lines[i];
// if (insidePostParameters) {
// this.body.writeLine(requestLine);
// } else if (requestLine.trim().equals("")
// || requestLine.trim().equals("\r")) {
// // POST parameters are separated from the headers by a blank
// // line
// insidePostParameters = true;
// }
// }
} catch (IOException e) {
UsageException errorVar = new UsageException("Cannot parse request: ", e);
ErrorMgr.addError(errorVar);
throw errorVar;
}
// Read a set of characters from the socket
// StringBuffer request = new StringBuffer(2048);
// int i;
// byte[] buffer = new byte[2048];
// try {
// i = input.read(buffer);
// }
// catch (IOException e) {
// e.printStackTrace();
// i = -1;
// }
// for (int j=0; j<i; j++) {
// request.append((char) buffer[j]);
// }
// uri = parseUri(request.toString());
}
public String getScheme() {
return "http"; // @NoChangeOnCopy
}
public String getRemoteAddr() {
return "127.0.0.1";
}
public String getRemoteHost() {
return "localhost";
}
public String getProtocol() {
return "HTTP"; // @NoChangeOnCopy
}
public String getServerName() {
return this.serverName;
}
public void setServerName(String name) {
this.serverName = name;
}
public String getServerPort() {
return this.serverPort;
}
public void setServerPort(String port) {
this.serverPort = port;
}
public HTTPBaseResponse send(String methodName, String url) {
this.setURL(url);
this.setMethod(methodName);
return this.send();
}
public HTTPBaseResponse send(String url) {
this.setURL(url);
return this.send();
}
public Proxy getProxy() {
return proxy;
}
public void setProxy(Proxy proxy) {
this.proxy = proxy;
}
/**
* The GetQueryString method returns the query part of the URL.
*
* @return String - the query part of the URL
*/
public String getQueryString() {
return queryString;
}
/**
* The setQueryString method sets the query value of the request.
*
* @param queryString
* the value parameter specifies the query value of the request.
*/
public void setQueryString(String queryString) {
this.queryString = queryString;
if (url != null) {
// need to reset the url
String fullURL = this.URLstring;
if (StringUtils.hasText(queryString)) {
fullURL = fullURL + "?" + queryString;
}
try {
this.url = new URL(fullURL);
} catch (MalformedURLException e) {
UsageException errorVar = new UsageException("Malformed URL", e);
ErrorMgr.addError(errorVar);
throw errorVar;
}
}
}
}