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

org.eclipse.jetty.toolchain.test.http.SimpleHttpParser Maven / Gradle / Ivy

//
//  ========================================================================
//  Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

package org.eclipse.jetty.toolchain.test.http;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import java.io.BufferedReader;
import java.io.EOFException;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * A simple http parser implementation to be used in tests. It's feed with a BufferedReader and will read from that
 * reader until the http response has been fully read.
 *
 * It returns a {@link SimpleHttpResponse} object that grants access to the response code,
 * headers and body of the parsed response.
 *
 * This class can easily be extended to provide more features. However keep in mind that it's primary focus is to
 * keep parsing as simple as possible.
 */
public class SimpleHttpParser
{
    /**
     * Reads from the given {@link BufferedReader} and returns the parsed response in a {@link SimpleHttpResponse}
     * object.
     *
     * @param reader
     *            the inputReader to parse the response from
     * @return {@link SimpleHttpResponse} a {@link SimpleHttpResponse} object representing the parsed response
     * @throws IOException
     *             if unable to read/parse the raw lines of http
     */
    public SimpleHttpResponse readResponse(BufferedReader reader) throws IOException
    {
        // Simplified parser for HTTP responses
        String line = reader.readLine();
        if (line == null)
            throw new EOFException();
        Matcher responseLine = Pattern.compile("HTTP/1.1" + "\\s+(\\d+)").matcher(line);
        assertThat("http version is 1.1",responseLine.lookingAt(),is(true));
        String code = responseLine.group(1);

        Map headers = new LinkedHashMap();
        while ((line = reader.readLine()) != null)
        {
            if (line.trim().length() == 0)
                break;

            parseHeader(line,headers);
        }

        StringBuilder body;
        if (headers.containsKey("content-length"))
        {
            body = parseContentLengthDelimitedBody(reader,headers);
        }
        else if ("chunked".equals(headers.get("transfer-encoding")))
        {
            body = parseChunkedBody(reader);
        }
        else
        {
            body = parseEOFDelimitedBody(reader,headers);
        }

        return new SimpleHttpResponse(code,headers,body.toString().trim());
    }

    private void parseHeader(String line, Map headers)
    {
        Matcher header = Pattern.compile("([^:]+):\\s*(.*)").matcher(line);
        assertTrue(header.lookingAt());
        String headerName = header.group(1);
        String headerValue = header.group(2);
        headers.put(headerName.toLowerCase(),headerValue.toLowerCase());
    }

    private StringBuilder parseContentLengthDelimitedBody(BufferedReader reader, Map headers) throws IOException
    {
        StringBuilder body;
        int readLen = 0;
        int length = Integer.parseInt(headers.get("content-length"));
        body = new StringBuilder(length);
        try
        {
            //TODO: UTF-8 reader from joakim
            for (int i = 0; i < length; ++i)
            {
                char c = (char)reader.read();
                body.append(c);
                readLen++;
            }

        }
        catch (SocketTimeoutException e)
        {
            System.err.printf("Read %,d bytes (out of an expected %,d bytes)%n",readLen,length);
            throw e;
        }
        return body;
    }

    private StringBuilder parseChunkedBody(BufferedReader reader) throws IOException
    {
        StringBuilder body;
        String line;
        body = new StringBuilder(64 * 1024);
        while ((line = reader.readLine()) != null)
        {
            if ("0".equals(line))
            {
                line = reader.readLine();
                assertThat("There's no more content after as 0 indicated the final chunk",line,is(""));
                break;
            }

            int length = Integer.parseInt(line,16);
            //TODO: UTF-8 reader from joakim
            for (int i = 0; i < length; ++i)
            {
                char c = (char)reader.read();
                body.append(c);
            }
            reader.readLine();
            // assertThat("chunk is followed by an empty line", line, is("")); //TODO: is this right? - NO.  Don't
            // think you can really do chunks with read line generally, but maybe for this test is OK.
        }
        return body;
    }

    private StringBuilder parseEOFDelimitedBody(BufferedReader reader, Map headers) throws IOException
    {
        StringBuilder body;

        // read until EOF
        body = new StringBuilder();
        while (true)
        {
            //TODO: UTF-8 reader from joakim
            int read = reader.read();
            if (read == -1)
                break;
            char c = (char)read;
            body.append(c);
        }
        return body;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy