All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.codemagi.burp.parser.HttpRequest Maven / Gradle / Ivy

Go to download

The Burp Suite Utils project provides developers with APIs for building Burp Suite Extensions.

The newest version!
package com.codemagi.burp.parser;

import burp.ICookie;
import burp.IParameter;
import burp.impl.Cookie;
import burp.impl.Parameter;
import com.codemagi.burp.Utils;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.regex.*;

/**
 * Generic http request parser. Messages are of the general form:
 * 
 * status_line
 * header: value
 * header: value
 * header: value
 * ...
 * 
where status_line is something like *
GET /foobar HTTP/1.0
* *

* This class will parse all the header/value pairs and store them in a * ({@link java.util.LinkedHashMap}), and parse the first line as 'command'. The * LinkedHashMap will assure that an iterator will visit the fields in * the order the were originally sent/parsed. * * Note that parameters sent in the body of POST requests are not parsed. * Instead they are stored as-is in the 'body' member. * * @author August Detlefsen [augustd at codemagi dot com] */ public class HttpRequest { //protected URL url; protected String method; protected String path; protected String version; protected String body = ""; protected LinkedHashMap headers = new LinkedHashMap<>(); protected LinkedHashMap sortedHeaders = null; protected LinkedHashMap sortedParams = null; protected List parameters = new ArrayList<>(); public final static char CR = (char) 0x0D; public final static char LF = (char) 0x0A; public final static String CRLF = "" + CR + LF; /** * Private no-argument constructor is only used internally by static factory * methods. */ private HttpRequest() { } /** * Shorthand constructor for creating an HttpMessage from a URL. * * This method will create an HTTP/1.1 GET request with the URI passed in * the 'url' parameter. A 'Host' header will be set with the host portion of * the 'url' parameter. * * @param url The URL to construct a request for */ public HttpRequest(URL url) { this(url, "GET"); } /** * Constructor for creating an HttpMessage from a URL and an HTTP method. * * This method will create an HTTP/1.1 request with the URI passed in the * 'url' parameter. A 'Host' header will be set with the host portion of the * 'url' parameter * * @param url The URL to construct a request for * @param method The HTTP method to use for the new request */ public HttpRequest(URL url, String method) { this.method = method; //set the uri of the request this.path = url.getPath(); if (!Utils.isEmpty(url.getQuery())) { parseParameters(url.getQuery()); } this.version = "HTTP/1.1"; this.setHeader("Host", url.getHost()); } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public String getVersion() { return version; } /** * * @return The body of the POST request, if there is one */ public String getBody() { return Utils.noNulls(body); } /** * Converts a GET request to a POST request: changes request method and * moves any URL parameters to the HTTP message body. */ public void convertToPost() { this.method = "POST"; //add parameters, if available StringBuilder bodyBuilder = new StringBuilder(); boolean first = true; for (Parameter param : parameters) { if (!first) { bodyBuilder.append("&"); } bodyBuilder.append(param.getName()); bodyBuilder.append("="); bodyBuilder.append(param.getValue()); first = false; } this.body = bodyBuilder.toString(); this.parameters = new ArrayList<>(); this.sortedParams = null; } /** * Converts a GET request to a Multipart/form-data request: changes request * method to POST, adds Content-Type/boundary header and moves any URL * parameters to the HTTP message body, formatted as a multipart request. */ public void convertToMultipart() { this.method = "POST"; String boundary = UUID.randomUUID().toString().replaceAll("-", ""); setHeader("Content-Type", "multipart/form-data; boundary=----" + boundary); //add parameters, if available StringBuilder bodyBuilder = new StringBuilder(); for (Parameter param : parameters) { bodyBuilder.append("------").append(boundary).append(CRLF); bodyBuilder.append("Content-Disposition: form-data; name=\"").append(param.getName()).append("\""); if (param.getType() == IParameter.PARAM_MULTIPART_ATTR) { bodyBuilder.append("; filename=\"").append(param.getFilename()).append("\"").append(CRLF); bodyBuilder.append("Content-Type: ").append(param.getContentType()); } bodyBuilder.append(CRLF).append(CRLF); bodyBuilder.append(param.getValue()); bodyBuilder.append(CRLF); } bodyBuilder.append("------").append(boundary).append("--").append(CRLF); this.body = bodyBuilder.toString(); this.parameters = new ArrayList<>(); this.sortedParams = null; } /** * Returns the map of HTTP headers contained in this message, sorted by * lower case character order. * * If the map of sorted headers has already been created that will be * returned. Otherwise, a new LinkedHashMap will be constructed and cached * for future use. * * @return A LinkedHashMap containing the sorted HTTP headers, using the * header names as the hash key * @see java.util.LinkedHashMap */ public LinkedHashMap getHeadersSorted() { if (sortedHeaders != null) { return sortedHeaders; } sortedHeaders = new LinkedHashMap(headers.size()); List headerEntries = new ArrayList(headers.entrySet()); Collections.sort(headerEntries, new HeaderComparator()); for (Map.Entry entry : headerEntries) { String name = (String) entry.getKey(); String value = (String) entry.getValue(); sortedHeaders.put(name, value); } return sortedHeaders; } /** * Sets a header field value based on its name. * * @param name The header name to set * @param value The header value to set * @return the previous value or null if the field was previously unset. */ public final String setHeader(String name, String value) { sortedHeaders = null; return headers.put(name, value); } public String getHeader(String name) { return headers.get(name); } /** * Sets the Content-Length header to the current size of the request body * * @return The length of the body content */ public int setContentLength() { int contentLength = Utils.noNulls(body).length(); setHeader("Content-Length", contentLength + ""); return contentLength; } /** * Gets the value of the Cookie header * * @return A list containing Cookie objects parsed from the internal string * representation */ public List getCookies() { String cookies = getHeader("Cookie"); List output = new ArrayList<>(); if (Utils.isEmpty(cookies)) { return output; } for (String cookieStr : cookies.split("[; ]+")) { String[] pair = cookieStr.split("=", 2); output.add(new Cookie(pair[0], pair[1])); } return output; } public void setCookies(List cookies) { StringBuilder buffer = new StringBuilder(cookies.size() * 40); for (ICookie cookie : cookies) { buffer.append(cookie.getName()).append("=").append(cookie.getValue()).append("; "); } setHeader("Cookie", buffer.toString()); } public void addCookie(ICookie newCookie) { if (newCookie == null || Utils.isEmpty(newCookie.getName())) { return; } //check if this cookie exists already boolean found = false; List cookies = getCookies(); for (ICookie oldCookie : cookies) { if (newCookie.getName().equals(oldCookie.getName())) { //update old cookie with new value Collections.replaceAll(cookies, oldCookie, newCookie); found = true; break; } } if (!found) { //add new cookie cookies.add(newCookie); } //set the cookie array back into this object's String representation setCookies(cookies); } public void addCookie(String name, String value) { addCookie(new Cookie(name, value)); } /** * Sets a parameter value based on its name. * * @param name The parameter name to set * @param value The parameter value to set */ public void setParameter(String name, String value) { sortedParams = null; parameters.add(new Parameter(name, value)); } /** * Sets a parameter using a Parameter object. * * @param param The parameter to add */ public void setParameter(Parameter param) { sortedParams = null; parameters.add(param); } /** * Sets the body of the HTTP request and updates the Content-Length header. * NOTE: This value will override any parameters previously set * * @param body The new request body */ public void setBody(String body) { this.body = body; this.setContentLength(); } /** * Returns the map of http parameters contained in this message, sorted by * lower case character order. * * If the map of sorted parameters has already been created that will be * returned. Otherwise, a new LinkedHashMap will be constructed and cached * for future use. * * @return A LinkedHashMap containing the sorted HTTP parameters, using the * parameter name as the key * @see java.util.LinkedHashMap */ public LinkedHashMap getParametersSorted() { if (sortedParams != null) { return sortedParams; } sortedParams = new LinkedHashMap<>(parameters.size()); List entries = new ArrayList(parameters); Collections.sort(entries, new ParameterComparator()); for (Parameter entry : entries) { String name = (String) entry.getName(); String value = (String) entry.getValue(); sortedParams.put(name, value); } return sortedParams; } /** * Parses a new HttpMessage using {@link #parse(InputStream)} with out as * null. * * @param in The InputStream to parse * @return An HttpMessage object parsed from the InputStream * @throws java.io.IOException Any IO Exceptions will be thrown */ public static HttpRequest parseMessage(InputStream in) throws IOException { HttpRequest m = new HttpRequest(); m.parse(in); return m; } /** * Parses a new HttpMessage using {@link #parse(InputStream)} with out as * null. * * @param in The array of bytes to parse * @return An HttpMessage object parsed from the array * @throws java.io.IOException Any IO Exceptions will be thrown */ public static HttpRequest parseMessage(byte[] in) throws IOException { HttpRequest m = new HttpRequest(); m.parse(new ByteArrayInputStream(in)); return m; } public static HttpRequest parseMessage(String in) throws IOException { if (in != null) { return parseMessage(in.getBytes()); } return new HttpRequest(); } /** * Parses an http message from an input stream. The first line of input is * save in the protected command variable. The subsequent lines are * put into a linked hash as field/value pairs. Input is parsed until a * blank line is reached, after which any data should appear. * * @param in An InputStream containing a valid HTTP message */ private void parse(InputStream in) throws IOException { Pattern p = Pattern.compile(":"); BufferedReader bin = new BufferedReader(new InputStreamReader(in), 1); String currLine = bin.readLine(); //command = currLine; //parse the command to get the request method parseCommand(currLine); //parse headers currLine = bin.readLine(); while (currLine != null) { if (Utils.isEmpty(currLine)) { break; //we have reached the end of the headers } //split the headers into name-value pairs String[] split = currLine.split(": "); if (split.length < 2) { split = new String[] { split[0], "" }; } String headerName = Utils.trim(split[0]); String headerValue = Utils.trim(split[1]); headers.put(headerName, headerValue); currLine = bin.readLine(); } //parse the POST body, if there is one if (currLine != null) { StringBuilder bodyBuilder = new StringBuilder(); while ((currLine = bin.readLine()) != null) { bodyBuilder.append(currLine).append("\n"); } body = bodyBuilder.toString(); } } private void parseCommand(String command) { if (Utils.isEmpty(command)) { return; } String[] parts = command.split(" "); method = (parts.length > 0) ? parts[0] : ""; String uri = (parts.length > 1) ? parts[1] : ""; version = (parts.length > 2) ? parts[2] : ""; if (!Utils.isEmpty(uri)) { String[] split = uri.split("\\?"); path = (split.length > 0) ? split[0] : ""; //see if there are any query params if (split.length > 1) { parseParameters(split[1]); } } } /** * Parses a String of HTTP parameters into the internal parameters data * structure. No input checking or decoding is performed! * * @param input A String of HTTP parameters in the form of: * name1=value1&name2=value2&... */ private void parseParameters(String input) { for (String param : input.split("&")) { String[] pair = param.split("="); String name = (pair.length > 0) ? pair[0] : null; String value = (pair.length > 1) ? pair[1] : null; if (name != null) { setParameter(name, value); } } } @Override public String toString() { StringBuilder output = new StringBuilder(1024); //Add the command output.append(method).append(" "); output.append(path); //add parameters, if available if (!parameters.isEmpty()) { output.append("?"); boolean first = true; for (Parameter param : parameters) { if (!first) { output.append("&"); } output.append(param.getName()); output.append("="); output.append(Utils.noNulls(param.getValue())); first = false; } } output.append(" "); output.append(version).append("\r\n"); //add the headers for (Map.Entry header : headers.entrySet()) { output.append(header.getKey()); output.append(": "); output.append(Utils.noNulls(header.getValue())); output.append("\r\n"); } output.append("\r\n"); //add the request body output.append(getBody()); return output.toString(); } public byte[] getBytes() { return toString().getBytes(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy