1 /* 2 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/AbstractSessionInputBuffer.java $ 3 * $Revision: 576077 $ 4 * $Date: 2007-09-16 04:50:22 -0700 (Sun, 16 Sep 2007) $ 5 * 6 * ==================================================================== 7 * Licensed to the Apache Software Foundation (ASF) under one 8 * or more contributor license agreements. See the NOTICE file 9 * distributed with this work for additional information 10 * regarding copyright ownership. The ASF licenses this file 11 * to you under the Apache License, Version 2.0 (the 12 * "License"); you may not use this file except in compliance 13 * with the License. You may obtain a copy of the License at 14 * 15 * http://www.apache.org/licenses/LICENSE-2.0 16 * 17 * Unless required by applicable law or agreed to in writing, 18 * software distributed under the License is distributed on an 19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 * KIND, either express or implied. See the License for the 21 * specific language governing permissions and limitations 22 * under the License. 23 * ==================================================================== 24 * 25 * This software consists of voluntary contributions made by many 26 * individuals on behalf of the Apache Software Foundation. For more 27 * information on the Apache Software Foundation, please see 28 * <http://www.apache.org/>. 29 * 30 */ 31 32 package org.apache.http.impl.io; 33 34 import java.io.IOException; 35 import java.io.InputStream; 36 37 import org.apache.http.io.SessionInputBuffer; 38 import org.apache.http.io.HttpTransportMetrics; 39 import org.apache.http.params.CoreConnectionPNames; 40 import org.apache.http.params.HttpParams; 41 import org.apache.http.params.HttpProtocolParams; 42 import org.apache.http.protocol.HTTP; 43 import org.apache.http.util.ByteArrayBuffer; 44 import org.apache.http.util.CharArrayBuffer; 45 46 /** 47 * Abstract base class for session input buffers that stream data 48 * from a {@link InputStream}. 49 * 50 * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a> 51 * 52 */ 53 public abstract class AbstractSessionInputBuffer implements SessionInputBuffer { 54 55 private InputStream instream; 56 private byte[] buffer; 57 private int bufferpos; 58 private int bufferlen; 59 60 private ByteArrayBuffer linebuffer = null; 61 62 private String charset = HTTP.US_ASCII; 63 private boolean ascii = true; 64 private int maxLineLen = -1; 65 66 private HttpTransportMetricsImpl metrics; 67 init(final InputStream instream, int buffersize, final HttpParams params)68 protected void init(final InputStream instream, int buffersize, final HttpParams params) { 69 if (instream == null) { 70 throw new IllegalArgumentException("Input stream may not be null"); 71 } 72 if (buffersize <= 0) { 73 throw new IllegalArgumentException("Buffer size may not be negative or zero"); 74 } 75 if (params == null) { 76 throw new IllegalArgumentException("HTTP parameters may not be null"); 77 } 78 this.instream = instream; 79 this.buffer = new byte[buffersize]; 80 this.bufferpos = 0; 81 this.bufferlen = 0; 82 this.linebuffer = new ByteArrayBuffer(buffersize); 83 this.charset = HttpProtocolParams.getHttpElementCharset(params); 84 this.ascii = this.charset.equalsIgnoreCase(HTTP.US_ASCII) 85 || this.charset.equalsIgnoreCase(HTTP.ASCII); 86 this.maxLineLen = params.getIntParameter(CoreConnectionPNames.MAX_LINE_LENGTH, -1); 87 this.metrics = new HttpTransportMetricsImpl(); 88 } 89 fillBuffer()90 protected int fillBuffer() throws IOException { 91 // compact the buffer if necessary 92 if (this.bufferpos > 0) { 93 int len = this.bufferlen - this.bufferpos; 94 if (len > 0) { 95 System.arraycopy(this.buffer, this.bufferpos, this.buffer, 0, len); 96 } 97 this.bufferpos = 0; 98 this.bufferlen = len; 99 } 100 int l; 101 int off = this.bufferlen; 102 int len = this.buffer.length - off; 103 l = this.instream.read(this.buffer, off, len); 104 if (l == -1) { 105 return -1; 106 } else { 107 this.bufferlen = off + l; 108 this.metrics.incrementBytesTransferred(l); 109 return l; 110 } 111 } 112 hasBufferedData()113 protected boolean hasBufferedData() { 114 return this.bufferpos < this.bufferlen; 115 } 116 read()117 public int read() throws IOException { 118 int noRead = 0; 119 while (!hasBufferedData()) { 120 noRead = fillBuffer(); 121 if (noRead == -1) { 122 return -1; 123 } 124 } 125 return this.buffer[this.bufferpos++] & 0xff; 126 } 127 read(final byte[] b, int off, int len)128 public int read(final byte[] b, int off, int len) throws IOException { 129 if (b == null) { 130 return 0; 131 } 132 int noRead = 0; 133 while (!hasBufferedData()) { 134 noRead = fillBuffer(); 135 if (noRead == -1) { 136 return -1; 137 } 138 } 139 int chunk = this.bufferlen - this.bufferpos; 140 if (chunk > len) { 141 chunk = len; 142 } 143 System.arraycopy(this.buffer, this.bufferpos, b, off, chunk); 144 this.bufferpos += chunk; 145 return chunk; 146 } 147 read(final byte[] b)148 public int read(final byte[] b) throws IOException { 149 if (b == null) { 150 return 0; 151 } 152 return read(b, 0, b.length); 153 } 154 locateLF()155 private int locateLF() { 156 for (int i = this.bufferpos; i < this.bufferlen; i++) { 157 if (this.buffer[i] == HTTP.LF) { 158 return i; 159 } 160 } 161 return -1; 162 } 163 readLine(final CharArrayBuffer charbuffer)164 public int readLine(final CharArrayBuffer charbuffer) throws IOException { 165 if (charbuffer == null) { 166 throw new IllegalArgumentException("Char array buffer may not be null"); 167 } 168 this.linebuffer.clear(); 169 int noRead = 0; 170 boolean retry = true; 171 while (retry) { 172 // attempt to find end of line (LF) 173 int i = locateLF(); 174 if (i != -1) { 175 // end of line found. 176 if (this.linebuffer.isEmpty()) { 177 // the entire line is preset in the read buffer 178 return lineFromReadBuffer(charbuffer, i); 179 } 180 retry = false; 181 int len = i + 1 - this.bufferpos; 182 this.linebuffer.append(this.buffer, this.bufferpos, len); 183 this.bufferpos = i + 1; 184 } else { 185 // end of line not found 186 if (hasBufferedData()) { 187 int len = this.bufferlen - this.bufferpos; 188 this.linebuffer.append(this.buffer, this.bufferpos, len); 189 this.bufferpos = this.bufferlen; 190 } 191 noRead = fillBuffer(); 192 if (noRead == -1) { 193 retry = false; 194 } 195 } 196 if (this.maxLineLen > 0 && this.linebuffer.length() >= this.maxLineLen) { 197 throw new IOException("Maximum line length limit exceeded"); 198 } 199 } 200 if (noRead == -1 && this.linebuffer.isEmpty()) { 201 // indicate the end of stream 202 return -1; 203 } 204 return lineFromLineBuffer(charbuffer); 205 } 206 lineFromLineBuffer(final CharArrayBuffer charbuffer)207 private int lineFromLineBuffer(final CharArrayBuffer charbuffer) 208 throws IOException { 209 // discard LF if found 210 int l = this.linebuffer.length(); 211 if (l > 0) { 212 if (this.linebuffer.byteAt(l - 1) == HTTP.LF) { 213 l--; 214 this.linebuffer.setLength(l); 215 } 216 // discard CR if found 217 if (l > 0) { 218 if (this.linebuffer.byteAt(l - 1) == HTTP.CR) { 219 l--; 220 this.linebuffer.setLength(l); 221 } 222 } 223 } 224 l = this.linebuffer.length(); 225 if (this.ascii) { 226 charbuffer.append(this.linebuffer, 0, l); 227 } else { 228 // This is VERY memory inefficient, BUT since non-ASCII charsets are 229 // NOT meant to be used anyway, there's no point optimizing it 230 String s = new String(this.linebuffer.buffer(), 0, l, this.charset); 231 charbuffer.append(s); 232 } 233 return l; 234 } 235 lineFromReadBuffer(final CharArrayBuffer charbuffer, int pos)236 private int lineFromReadBuffer(final CharArrayBuffer charbuffer, int pos) 237 throws IOException { 238 int off = this.bufferpos; 239 int len; 240 this.bufferpos = pos + 1; 241 // BEGIN android-changed 242 // The first test below was fixed to not try to skip beyond the 243 // start of the live part of the buffer. 244 if (pos > off && this.buffer[pos - 1] == HTTP.CR) { 245 // skip CR if found 246 pos--; 247 } 248 // END android-changed 249 len = pos - off; 250 if (this.ascii) { 251 charbuffer.append(this.buffer, off, len); 252 } else { 253 // This is VERY memory inefficient, BUT since non-ASCII charsets are 254 // NOT meant to be used anyway, there's no point optimizing it 255 String s = new String(this.buffer, off, len, this.charset); 256 charbuffer.append(s); 257 } 258 return len; 259 } 260 readLine()261 public String readLine() throws IOException { 262 CharArrayBuffer charbuffer = new CharArrayBuffer(64); 263 int l = readLine(charbuffer); 264 if (l != -1) { 265 return charbuffer.toString(); 266 } else { 267 return null; 268 } 269 } 270 getMetrics()271 public HttpTransportMetrics getMetrics() { 272 return this.metrics; 273 } 274 275 } 276