michael@0: /*
michael@0: * ====================================================================
michael@0: * Licensed to the Apache Software Foundation (ASF) under one
michael@0: * or more contributor license agreements. See the NOTICE file
michael@0: * distributed with this work for additional information
michael@0: * regarding copyright ownership. The ASF licenses this file
michael@0: * to you under the Apache License, Version 2.0 (the
michael@0: * "License"); you may not use this file except in compliance
michael@0: * with the License. You may obtain a copy of the License at
michael@0: *
michael@0: * http://www.apache.org/licenses/LICENSE-2.0
michael@0: *
michael@0: * Unless required by applicable law or agreed to in writing,
michael@0: * software distributed under the License is distributed on an
michael@0: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
michael@0: * KIND, either express or implied. See the License for the
michael@0: * specific language governing permissions and limitations
michael@0: * under the License.
michael@0: * ====================================================================
michael@0: *
michael@0: * This software consists of voluntary contributions made by many
michael@0: * individuals on behalf of the Apache Software Foundation. For more
michael@0: * information on the Apache Software Foundation, please see
michael@0: *
michael@0: * {@link #readLine(CharArrayBuffer)} and {@link #readLine()} methods of this michael@0: * class treat a lone LF as valid line delimiters in addition to CR-LF required michael@0: * by the HTTP specification. michael@0: * michael@0: *
michael@0: * The following parameters can be used to customize the behavior of this michael@0: * class: michael@0: *
-1
is returned. This method blocks until input
michael@0: * data is available, end of file is detected, or an exception is thrown.
michael@0: *
michael@0: * This method treats a lone LF as a valid line delimiters in addition
michael@0: * to CR-LF required by the HTTP specification.
michael@0: *
michael@0: * @param charbuffer the line buffer.
michael@0: * @return one line of characters
michael@0: * @exception IOException if an I/O error occurs.
michael@0: */
michael@0: public int readLine(final CharArrayBuffer charbuffer) throws IOException {
michael@0: if (charbuffer == null) {
michael@0: throw new IllegalArgumentException("Char array buffer may not be null");
michael@0: }
michael@0: int noRead = 0;
michael@0: boolean retry = true;
michael@0: while (retry) {
michael@0: // attempt to find end of line (LF)
michael@0: int i = locateLF();
michael@0: if (i != -1) {
michael@0: // end of line found.
michael@0: if (this.linebuffer.isEmpty()) {
michael@0: // the entire line is preset in the read buffer
michael@0: return lineFromReadBuffer(charbuffer, i);
michael@0: }
michael@0: retry = false;
michael@0: int len = i + 1 - this.bufferpos;
michael@0: this.linebuffer.append(this.buffer, this.bufferpos, len);
michael@0: this.bufferpos = i + 1;
michael@0: } else {
michael@0: // end of line not found
michael@0: if (hasBufferedData()) {
michael@0: int len = this.bufferlen - this.bufferpos;
michael@0: this.linebuffer.append(this.buffer, this.bufferpos, len);
michael@0: this.bufferpos = this.bufferlen;
michael@0: }
michael@0: noRead = fillBuffer();
michael@0: if (noRead == -1) {
michael@0: retry = false;
michael@0: }
michael@0: }
michael@0: if (this.maxLineLen > 0 && this.linebuffer.length() >= this.maxLineLen) {
michael@0: throw new IOException("Maximum line length limit exceeded");
michael@0: }
michael@0: }
michael@0: if (noRead == -1 && this.linebuffer.isEmpty()) {
michael@0: // indicate the end of stream
michael@0: return -1;
michael@0: }
michael@0: return lineFromLineBuffer(charbuffer);
michael@0: }
michael@0:
michael@0: /**
michael@0: * Reads a complete line of characters up to a line delimiter from this
michael@0: * session buffer. The line delimiter itself is discarded. If no char is
michael@0: * available because the end of the stream has been reached,
michael@0: * null
is returned. This method blocks until input data is
michael@0: * available, end of file is detected, or an exception is thrown.
michael@0: *
michael@0: * This method treats a lone LF as a valid line delimiters in addition michael@0: * to CR-LF required by the HTTP specification. michael@0: * michael@0: * @return HTTP line as a string michael@0: * @exception IOException if an I/O error occurs. michael@0: */ michael@0: private int lineFromLineBuffer(final CharArrayBuffer charbuffer) michael@0: throws IOException { michael@0: // discard LF if found michael@0: int l = this.linebuffer.length(); michael@0: if (l > 0) { michael@0: if (this.linebuffer.byteAt(l - 1) == HTTP.LF) { michael@0: l--; michael@0: this.linebuffer.setLength(l); michael@0: } michael@0: // discard CR if found michael@0: if (l > 0) { michael@0: if (this.linebuffer.byteAt(l - 1) == HTTP.CR) { michael@0: l--; michael@0: this.linebuffer.setLength(l); michael@0: } michael@0: } michael@0: } michael@0: l = this.linebuffer.length(); michael@0: if (this.ascii) { michael@0: charbuffer.append(this.linebuffer, 0, l); michael@0: } else { michael@0: // This is VERY memory inefficient, BUT since non-ASCII charsets are michael@0: // NOT meant to be used anyway, there's no point optimizing it michael@0: String s = new String(this.linebuffer.buffer(), 0, l, this.charset); michael@0: l = s.length(); michael@0: charbuffer.append(s); michael@0: } michael@0: this.linebuffer.clear(); michael@0: return l; michael@0: } michael@0: michael@0: private int lineFromReadBuffer(final CharArrayBuffer charbuffer, int pos) michael@0: throws IOException { michael@0: int off = this.bufferpos; michael@0: int len; michael@0: this.bufferpos = pos + 1; michael@0: if (pos > 0 && this.buffer[pos - 1] == HTTP.CR) { michael@0: // skip CR if found michael@0: pos--; michael@0: } michael@0: len = pos - off; michael@0: if (this.ascii) { michael@0: charbuffer.append(this.buffer, off, len); michael@0: } else { michael@0: // This is VERY memory inefficient, BUT since non-ASCII charsets are michael@0: // NOT meant to be used anyway, there's no point optimizing it michael@0: String s = new String(this.buffer, off, len, this.charset); michael@0: charbuffer.append(s); michael@0: len = s.length(); michael@0: } michael@0: return len; michael@0: } michael@0: michael@0: public String readLine() throws IOException { michael@0: CharArrayBuffer charbuffer = new CharArrayBuffer(64); michael@0: int l = readLine(charbuffer); michael@0: if (l != -1) { michael@0: return charbuffer.toString(); michael@0: } else { michael@0: return null; michael@0: } michael@0: } michael@0: michael@0: public HttpTransportMetrics getMetrics() { michael@0: return this.metrics; michael@0: } michael@0: michael@0: }