/*********************************************************************** * * $CVSHeader$ * * This file is part of WebScarab, an Open Web Application Security * Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2004 Rogan Dawes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Getting Source * ============== * * Source for this application is maintained at Sourceforge.net, a * repository for free software projects. * * For details, please see http://www.sourceforge.net/projects/owasp * */ /* * Request.java * * Created on May 12, 2003, 11:12 PM */ package org.owasp.webscarab.model; import java.io.InputStream; import java.io.OutputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.net.MalformedURLException; import java.net.SocketTimeoutException; import java.text.ParseException; /** This class represents a request that can be sent to an HTTP server. * @author rdawes */ public class Request extends Message { private String _method = "GET"; private HttpUrl _url = null; private String _version = "HTTP/1.0"; /** Creates a new instance of Request */ public Request() { } /** * Creates a new Request, which is a copy of the supplied Request * @param req the request to copy */ public Request(Request req) { _method = req.getMethod(); _url = req.getURL(); _version = req.getVersion(); setHeaders(req.getHeaders()); setContent(req.getContent()); } /** * initialises the Request from the supplied InputStream * @param is the InputStream to read from * @throws IOException propagates any exceptions thrown by the InputStream */ public void read(InputStream is) throws IOException { read(is, null); } /** * initialises the Request from the supplied InputStream, using the supplied Url * as a base. This will generally be useful where we are acting as a web server, * and reading a line like "GET / HTTP/1.0". The request Url is then created * relative to the supplied Url. * @param is the InputStream to read from * @param base the base Url to use for relative Urls * @throws IOException propagates any IOExceptions thrown by the InputStream */ public void read(InputStream is, HttpUrl base) throws IOException { String line = null; _logger.finer("Base: " + base); try { line = readLine(is); _logger.finest("Request: " + line); } catch (SocketTimeoutException ste) { // System.err.println("Read timed out. Closing connection"); return; } if (line == null || line.equals("")) { // System.err.println("Client closed connection!"); return; } String[] parts = line.split(" "); if (parts.length == 2 || parts.length == 3) { setMethod(parts[0]); if (getMethod().equalsIgnoreCase("CONNECT")) { setURL(new HttpUrl("https://" + parts[1] + "/")); } else { // supports creating an absolute url from a relative one setURL(new HttpUrl(base, parts[1])); } } else { throw new IOException("Invalid request line reading from the InputStream '"+line+"'"); } if (parts.length == 3) { setVersion(parts[2]); } else { setVersion("HTTP/0.9"); } // Read the rest of the message headers and to the start of the body super.read(is); if (_method.equals("CONNECT") || _method.equals("GET") || _method.equals("HEAD") || _method.equals("TRACE")) { // These methods cannot include a message body setNoBody(); } } /** * parses a string representation of a request * @param string the string representing the request * @throws ParseException if there are any errors parsing the request */ public void parse(String string) throws ParseException { parse(new StringBuffer(string)); } /** * parses a string representation of a request * @param buff a StringBuffer containing the request. Note that the contents of the StringBuffer are consumed during parsing. * @throws ParseException if there are any errors parsing the request */ public void parse(StringBuffer buff) throws ParseException { String line = null; line = getLine(buff); String[] parts = line.split(" "); if (parts.length == 2 || parts.length == 3) { setMethod(parts[0]); try { if (getMethod().equalsIgnoreCase("CONNECT")) { setURL(new HttpUrl("https://" + parts[1] + "/")); } else { setURL(new HttpUrl(parts[1])); } } catch (MalformedURLException mue) { throw new ParseException("Malformed URL '" + parts[1] + "' : " + mue, parts[0].length()+1); } } else { throw new ParseException("Invalid request line '" + line + "'", 0); } if (parts.length == 3) { setVersion(parts[2]); } else { setVersion("HTTP/0.9"); } // Read the rest of the message headers and body super.parse(buff); if (_method.equals("CONNECT") || _method.equals("GET") || _method.equals("HEAD") || _method.equals("TRACE")) { // These methods cannot include a message body setNoBody(); } } /** * Writes the Request to the supplied OutputStream, in a format appropriate for * sending to an HTTP proxy. Uses the RFC CRLF string "\r\n" * @param os the OutputStream to write to * @throws IOException if the underlying stream throws any. */ public void write(OutputStream os) throws IOException { write(os,"\r\n"); } /** * Writes the Request to the supplied OutputStream, in a format appropriate for * sending to an HTTP proxy. Uses the supplied string to separate lines. * @param os the OutputStream to write to * @param crlf the string to use to separate the lines (usually a CRLF pair) * @throws IOException if the underlying stream throws any. */ public void write(OutputStream os, String crlf) throws IOException { if (_method == null || _url == null || _version == null) { System.err.println("Uninitialised Request!"); return; } os = new BufferedOutputStream(os); String requestLine = _method+" "+_url+" " + _version + crlf; os.write(requestLine.getBytes()); _logger.finer("Request: " + requestLine); super.write(os, crlf); os.flush(); } /** Writes the Request to the supplied OutputStream, in a format appropriate for * sending to the HTTP server itself. Uses the RFC CRLF string "\r\n" * @param os the OutputStream to write to * @throws IOException if the underlying stream throws any. */ public void writeDirect(OutputStream os) throws IOException { writeDirect(os, "\r\n"); } /** Writes the Request to the supplied OutputStream, in a format appropriate for * sending to the HTTP server itself. Uses the supplied string to separate lines. * @param os the OutputStream to write to * @param crlf the string to use to separate the lines (usually a CRLF pair) * @throws IOException if the underlying stream throws any. */ public void writeDirect(OutputStream os, String crlf) throws IOException { if (_method == null || _url == null || _version == null) { System.err.println("Uninitialised Request!"); return; } os = new BufferedOutputStream(os); String requestLine = _method + " " + _url.direct() + " " + _version; os.write((requestLine+crlf).getBytes()); _logger.finer("Request: " + requestLine); super.write(os, crlf); os.flush(); } /** * Sets the request method * @param method the method of the request (automatically converted to uppercase) */ public void setMethod(String method) { _method = method.toUpperCase(); } /** * gets the Request method * @return the request method */ public String getMethod() { return _method; } /** * Sets the Request URL * @param url the url */ public void setURL(HttpUrl url) { _url = url; } /** * Gets the Request URL * @return the request url */ public HttpUrl getURL() { return _url; } /** * Convenience method to search on parameters * @param search * @return true if parameters contains */ public boolean parameterSearch(String search) { String s = getURL().getQuery(); if(s == null) return false; return s.toLowerCase().indexOf(search) > -1; } /** * Sets the HTTP version supported * @param version the version of the request. Automatically converted to uppercase. */ public void setVersion(String version) { _version = version.toUpperCase(); } /** * gets the HTTP version * @return the version of the request */ public String getVersion() { return _version; } /** * returns a string representation of the Request, using a CRLF of "\r\n" * @return a string representation of the Request, using a CRLF of "\r\n" */ public String toString() { return toString("\r\n"); } /** * returns a string representation of the Request, using the supplied string to * separate lines * @param crlf the string to use to separate lines (usually CRLF) * @return a string representation of the Request */ public String toString(String crlf) { if (_method == null || _url == null || _version == null) { return "Unitialised Request!"; } StringBuffer buff = new StringBuffer(); buff.append(_method).append(" "); buff.append(_url).append(" "); buff.append(_version).append(crlf); buff.append(super.toString(crlf)); return buff.toString(); } public boolean equals(Object obj) { if (!(obj instanceof Request)) return false; Request req = (Request)obj; if (!getMethod().equals(req.getMethod())) return false; if (!getURL().equals(req.getURL())) return false; if (!getVersion().equals(req.getVersion())) return false; return super.equals(req); } }