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

org.tomitribe.simplehttp.SimpleHttpClient Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.tomitribe.simplehttp;

import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * Simple client for unit testing. It isn't robust, it isn't secure and
 * should not be used as the basis for production code. Its only purpose
 * is to do the bare minimum for the unit tests.
 */
public abstract class SimpleHttpClient {
    public static final String TEMP_DIR =
            System.getProperty("java.io.tmpdir");

    public static final String CR = "\r";
    public static final String LF = "\n";
    public static final String CRLF = CR + LF;

    public static final String INFO_100 = "HTTP/1.1 100 ";
    public static final String OK_200 = "HTTP/1.1 200 ";
    public static final String REDIRECT_302 = "HTTP/1.1 302 ";
    public static final String FAIL_400 = "HTTP/1.1 400 ";
    public static final String FORBIDDEN_403 = "HTTP/1.1 403 ";
    public static final String FAIL_404 = "HTTP/1.1 404 ";
    public static final String FAIL_405 = "HTTP/1.1 405 ";
    public static final String TIMEOUT_408 = "HTTP/1.1 408 ";
    public static final String FAIL_413 = "HTTP/1.1 413 ";
    public static final String FAIL_417 = "HTTP/1.1 417 ";
    public static final String FAIL_50X = "HTTP/1.1 50";
    public static final String FAIL_500 = "HTTP/1.1 500 ";
    public static final String FAIL_501 = "HTTP/1.1 501 ";

    private static final String CONTENT_LENGTH_HEADER_PREFIX =
            "Content-Length: ";

    protected static final String SESSION_COOKIE_NAME = "JSESSIONID";
    protected static final String SESSION_PARAMETER_NAME =
            SESSION_COOKIE_NAME.toLowerCase(Locale.ENGLISH);

    private static final String COOKIE_HEADER_PREFIX = "Set-Cookie: ";
    private static final String SESSION_COOKIE_HEADER_PREFIX =
            COOKIE_HEADER_PREFIX + SESSION_COOKIE_NAME + "=";

    private static final String REDIRECT_HEADER_PREFIX = "Location: ";
    protected static final String SESSION_PATH_PARAMETER_PREFIX =
            SESSION_PARAMETER_NAME + "=";
    protected static final String SESSION_PATH_PARAMETER_TAILS = CRLF + ";?";

    private static final String ELEMENT_HEAD = "<";
    private static final String ELEMENT_TAIL = ">";
    private static final String RESOURCE_TAG = "a";
    private static final String LOGIN_TAG = "form";

    private Socket socket;
    private Writer writer;
    private BufferedReader reader;
    private int port = 8080;
    private String hostname = "localhost";

    private String[] request;
    private boolean useContinue = false;
    private boolean useCookies = true;
    private int requestPause = 1000;

    private String responseLine;
    private List responseHeaders = new ArrayList();
    private String sessionId;
    private boolean useContentLength;
    private int contentLength;
    private String redirectUri;

    private String responseBody;
    private List bodyUriElements = null;

    public void setPort(int thePort) {
        port = thePort;
    }

    public void setRequest(String[] theRequest) {
        request = theRequest;
    }

    /*
     * Expect the server to reply with 100 Continue interim response
     */
    public void setUseContinue(boolean theUseContinueFlag) {
        useContinue = theUseContinueFlag;
    }

    public boolean getUseContinue() {
        return useContinue;
    }

    public void setUseCookies(boolean theUseCookiesFlag) {
        useCookies = theUseCookiesFlag;
    }

    public boolean getUseCookies() {
        return useCookies;
    }

    public void setRequestPause(int theRequestPause) {
        requestPause = theRequestPause;
    }

    public String getResponseLine() {
        return responseLine;
    }

    public List getResponseHeaders() {
        return responseHeaders;
    }

    public String getResponseBody() {
        return responseBody;
    }

    /**
     * Return opening tags of HTML elements that were extracted by the
     * {@link #extractUriElements()} method.
     *
     * 

* Note, that {@link #extractUriElements()} method has to be called * explicitly. * * @return List of HTML tags, accumulated by {@link #extractUriElements()} * method, or {@code null} if the method has not been called yet. */ public List getResponseBodyUriElements() { return bodyUriElements; } public void setUseContentLength(boolean b) { useContentLength = b; } public void setSessionId(String theSessionId) { sessionId = theSessionId; } public String getSessionId() { return sessionId; } public String getRedirectUri() { return redirectUri; } public String getHostname() { return hostname; } public void setHostname(String hostname) { this.hostname = hostname; } public void connect(int connectTimeout, int soTimeout) throws UnknownHostException, IOException { final String encoding = "ISO-8859-1"; SocketAddress addr = new InetSocketAddress(hostname, port); socket = new Socket(); socket.setSoTimeout(soTimeout); socket.connect(addr,connectTimeout); OutputStream os = createOutputStream(socket); writer = new OutputStreamWriter(os, encoding); InputStream is = socket.getInputStream(); Reader r = new InputStreamReader(is, encoding); reader = new BufferedReader(r); } public void connect() throws UnknownHostException, IOException { connect(0,0); } protected OutputStream createOutputStream(Socket socket) throws IOException { return socket.getOutputStream(); } public void processRequest() throws IOException, InterruptedException { processRequest(true); } public void processRequest(boolean wantBody) throws IOException, InterruptedException { sendRequest(); readResponse(wantBody); } /* * Send the component parts of the request * (be tolerant and simply skip null entries) */ public void sendRequest() throws InterruptedException, IOException { boolean first = true; for (String requestPart : request) { if (requestPart != null) { if (first) { first = false; } else { Thread.sleep(requestPause); } writer.write(requestPart); writer.flush(); } } } public void readResponse(boolean wantBody) throws IOException { // clear any residual data before starting on this response responseHeaders.clear(); responseBody = null; if (bodyUriElements != null) { bodyUriElements.clear(); } // Read the response status line responseLine = readLine(); // Is a 100 continue response expected? if (useContinue) { if (isResponse100()) { // Skip the blank after the 100 Continue response readLine(); // Now get the final response responseLine = readLine(); } else { throw new IOException("No 100 Continue response"); } } // Put the headers into a map, and process interesting ones processHeaders(); // Read the body, if requested and if one exists processBody(wantBody); if (isResponse408()) { // the session has timed out sessionId = null; } } /* * Accumulate the response headers into a map, and extract * interesting details at the same time */ private void processHeaders() throws IOException { // Reset response fields redirectUri = null; contentLength = -1; String line = readLine(); while ((line != null) && (line.length() > 0)) { responseHeaders.add(line); if (line.startsWith(CONTENT_LENGTH_HEADER_PREFIX)) { contentLength = Integer.parseInt(line.substring(16)); } else if (line.startsWith(COOKIE_HEADER_PREFIX)) { if (useCookies) { String temp = line.substring( SESSION_COOKIE_HEADER_PREFIX.length()); temp = temp.substring(0, temp.indexOf(';')); setSessionId(temp); } } else if (line.startsWith(REDIRECT_HEADER_PREFIX)) { redirectUri = line.substring(REDIRECT_HEADER_PREFIX.length()); } line = readLine(); } } /* * Read the body of the server response. Save its contents and * search it for uri-elements only if requested */ private void processBody(boolean wantBody) throws IOException { // Read the body, if one exists StringBuilder builder = new StringBuilder(); if (wantBody) { if (useContentLength && (contentLength > -1)) { char[] body = new char[contentLength]; reader.read(body); builder.append(body); } else { // not using content length, so just read it line by line String line = null; try { while ((line = readLine()) != null) { builder.append(line); } } catch (SocketException e) { // Ignore // May see a SocketException if the request hasn't been // fully read when the connection is closed as that may // trigger a TCP reset. } } } responseBody = builder.toString(); } /** * Scan the response body for opening tags of certain HTML elements * (<a>, <form>). If any are found, then accumulate them. * *

* Note: This method has the following limitations: a) It assumes that the * response is HTML. b) It searches for lowercase tags only. * * @see #getResponseBodyUriElements() */ public void extractUriElements() { bodyUriElements = new ArrayList(); if (responseBody.length() > 0) { int ix = 0; while ((ix = extractUriElement(responseBody, ix, RESOURCE_TAG)) > 0){ // loop } ix = 0; while ((ix = extractUriElement(responseBody, ix, LOGIN_TAG)) > 0){ // loop } } } /* * Scan an html body for a given html uri element, starting from the * given index into the source string. If any are found, simply * accumulate them as literal strings, including angle brackets. * note: nested elements will not be collected. * * @param body HTTP body of the response * @param startIx offset into the body to resume the scan (for iteration) * @param elementName to scan for (only one at a time) * @returns the index into the body to continue the scan (for iteration) */ private int extractUriElement(String body, int startIx, String elementName) { String detector = ELEMENT_HEAD + elementName + " "; int iStart = body.indexOf(detector, startIx); if (iStart > -1) { int iEnd = body.indexOf(ELEMENT_TAIL, iStart); if (iEnd < 0) { throw new IllegalArgumentException( "Response body check failure.\n" + "element [" + detector + "] is not terminated with [" + ELEMENT_TAIL + "]\nActual: [" + body + "]"); } String element = body.substring(iStart, iEnd); bodyUriElements.add(element); iStart += element.length(); } return iStart; } public String readLine() throws IOException { final String line = reader.readLine(); return line; } public void disconnect() throws IOException { writer.close(); reader.close(); socket.close(); } public void reset() { socket = null; writer = null; reader = null; request = null; requestPause = 1000; useContinue = false; resetResponse(); } public void resetResponse() { responseLine = null; responseHeaders = new ArrayList(); responseBody = null; } public boolean responseLineStartsWith(String expected) { String line = getResponseLine(); if (line == null) { return false; } return line.startsWith(expected); } public boolean isResponse100() { return responseLineStartsWith(INFO_100); } public boolean isResponse200() { return responseLineStartsWith(OK_200); } public boolean isResponse302() { return responseLineStartsWith(REDIRECT_302); } public boolean isResponse400() { return responseLineStartsWith(FAIL_400); } public boolean isResponse403() { return responseLineStartsWith(FORBIDDEN_403); } public boolean isResponse404() { return responseLineStartsWith(FAIL_404); } public boolean isResponse405() { return responseLineStartsWith(FAIL_405); } public boolean isResponse408() { return responseLineStartsWith(TIMEOUT_408); } public boolean isResponse413() { return responseLineStartsWith(FAIL_413); } public boolean isResponse417() { return responseLineStartsWith(FAIL_417); } public boolean isResponse50x() { return responseLineStartsWith(FAIL_50X); } public boolean isResponse500() { return responseLineStartsWith(FAIL_500); } public boolean isResponse501() { return responseLineStartsWith(FAIL_501); } public Socket getSocket() { return socket; } public abstract boolean isResponseBodyOK(); public abstract Exception doRequest(); }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy