001/*
002 * ====================================================================
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *   http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 * ====================================================================
020 *
021 * This software consists of voluntary contributions made by many
022 * individuals on behalf of the Apache Software Foundation.  For more
023 * information on the Apache Software Foundation, please see
024 * <http://www.apache.org/>.
025 *
026 */
027
028package org.apache.http.impl.conn;
029
030import java.io.IOException;
031
032import org.apache.commons.logging.Log;
033import org.apache.commons.logging.LogFactory;
034import org.apache.http.HttpException;
035import org.apache.http.HttpResponse;
036import org.apache.http.HttpResponseFactory;
037import org.apache.http.NoHttpResponseException;
038import org.apache.http.ProtocolException;
039import org.apache.http.StatusLine;
040import org.apache.http.config.MessageConstraints;
041import org.apache.http.impl.DefaultHttpResponseFactory;
042import org.apache.http.impl.io.AbstractMessageParser;
043import org.apache.http.io.SessionInputBuffer;
044import org.apache.http.message.LineParser;
045import org.apache.http.message.ParserCursor;
046import org.apache.http.params.HttpParams;
047import org.apache.http.util.Args;
048import org.apache.http.util.CharArrayBuffer;
049
050/**
051 * Lenient HTTP response parser implementation that can skip malformed data until
052 * a valid HTTP response message head is encountered.
053 *
054 * @since 4.2
055 */
056@SuppressWarnings("deprecation")
057public class DefaultHttpResponseParser extends AbstractMessageParser<HttpResponse> {
058
059    private final Log log = LogFactory.getLog(getClass());
060
061    private final HttpResponseFactory responseFactory;
062    private final CharArrayBuffer lineBuf;
063
064    /**
065     * @deprecated (4.3) use {@link DefaultHttpResponseParser#DefaultHttpResponseParser(
066     *   SessionInputBuffer, LineParser, HttpResponseFactory, MessageConstraints)}
067     */
068    @Deprecated
069    public DefaultHttpResponseParser(
070            final SessionInputBuffer buffer,
071            final LineParser parser,
072            final HttpResponseFactory responseFactory,
073            final HttpParams params) {
074        super(buffer, parser, params);
075        Args.notNull(responseFactory, "Response factory");
076        this.responseFactory = responseFactory;
077        this.lineBuf = new CharArrayBuffer(128);
078    }
079
080    /**
081     * Creates new instance of DefaultHttpResponseParser.
082     *
083     * @param buffer the session input buffer.
084     * @param lineParser the line parser. If {@code null}
085     *   {@link org.apache.http.message.BasicLineParser#INSTANCE} will be used.
086     * @param responseFactory HTTP response factory. If {@code null}
087     *   {@link DefaultHttpResponseFactory#INSTANCE} will be used.
088     * @param constraints the message constraints. If {@code null}
089     *   {@link MessageConstraints#DEFAULT} will be used.
090     *
091     * @since 4.3
092     */
093    public DefaultHttpResponseParser(
094            final SessionInputBuffer buffer,
095            final LineParser lineParser,
096            final HttpResponseFactory responseFactory,
097            final MessageConstraints constraints) {
098        super(buffer, lineParser, constraints);
099        this.responseFactory = responseFactory != null ? responseFactory :
100                DefaultHttpResponseFactory.INSTANCE;
101        this.lineBuf = new CharArrayBuffer(128);
102    }
103
104    /**
105     * Creates new instance of DefaultHttpResponseParser.
106     *
107     * @param buffer the session input buffer.
108     * @param constraints the message constraints. If {@code null}
109     *   {@link MessageConstraints#DEFAULT} will be used.
110     *
111     * @since 4.3
112     */
113    public DefaultHttpResponseParser(
114        final SessionInputBuffer buffer, final MessageConstraints constraints) {
115        this(buffer, null, null, constraints);
116    }
117
118    /**
119     * Creates new instance of DefaultHttpResponseParser.
120     *
121     * @param buffer the session input buffer.
122     *
123     * @since 4.3
124     */
125    public DefaultHttpResponseParser(final SessionInputBuffer buffer) {
126        this(buffer, null, null, MessageConstraints.DEFAULT);
127    }
128
129    @Override
130    protected HttpResponse parseHead(
131            final SessionInputBuffer sessionBuffer) throws IOException, HttpException {
132        //read out the HTTP status string
133        int count = 0;
134        ParserCursor cursor = null;
135        do {
136            // clear the buffer
137            this.lineBuf.clear();
138            final int i = sessionBuffer.readLine(this.lineBuf);
139            if (i == -1 && count == 0) {
140                // The server just dropped connection on us
141                throw new NoHttpResponseException("The target server failed to respond");
142            }
143            cursor = new ParserCursor(0, this.lineBuf.length());
144            if (lineParser.hasProtocolVersion(this.lineBuf, cursor)) {
145                // Got one
146                break;
147            } else if (i == -1 || reject(this.lineBuf, count)) {
148                // Giving up
149                throw new ProtocolException("The server failed to respond with a " +
150                        "valid HTTP response");
151            }
152            if (this.log.isDebugEnabled()) {
153                this.log.debug("Garbage in response: " + this.lineBuf.toString());
154            }
155            count++;
156        } while(true);
157        //create the status line from the status string
158        final StatusLine statusline = lineParser.parseStatusLine(this.lineBuf, cursor);
159        return this.responseFactory.newHttpResponse(statusline, null);
160    }
161
162    protected boolean reject(final CharArrayBuffer line, final int count) {
163        return false;
164    }
165
166}