pt 1 in reviving the android build (copied from pm 28a1, wish me luck)

This commit is contained in:
wuggy 2026-04-08 15:43:25 -07:00
commit d7788a6d6d
4249 changed files with 468189 additions and 0 deletions

View file

@ -0,0 +1,284 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpException;
import ch.boye.httpclientandroidlib.HttpMessage;
import ch.boye.httpclientandroidlib.MessageConstraintException;
import ch.boye.httpclientandroidlib.ParseException;
import ch.boye.httpclientandroidlib.ProtocolException;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.config.MessageConstraints;
import ch.boye.httpclientandroidlib.io.HttpMessageParser;
import ch.boye.httpclientandroidlib.io.SessionInputBuffer;
import ch.boye.httpclientandroidlib.message.BasicLineParser;
import ch.boye.httpclientandroidlib.message.LineParser;
import ch.boye.httpclientandroidlib.params.HttpParamConfig;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* Abstract base class for HTTP message parsers that obtain input from
* an instance of {@link SessionInputBuffer}.
*
* @since 4.0
*/
@SuppressWarnings("deprecation")
@NotThreadSafe
public abstract class AbstractMessageParser<T extends HttpMessage> implements HttpMessageParser<T> {
private static final int HEAD_LINE = 0;
private static final int HEADERS = 1;
private final SessionInputBuffer sessionBuffer;
private final MessageConstraints messageConstraints;
private final List<CharArrayBuffer> headerLines;
protected final LineParser lineParser;
private int state;
private T message;
/**
* Creates an instance of AbstractMessageParser.
*
* @param buffer the session input buffer.
* @param parser the line parser.
* @param params HTTP parameters.
*
* @deprecated (4.3) use {@link AbstractMessageParser#AbstractMessageParser(SessionInputBuffer,
* LineParser, MessageConstraints)}
*/
@Deprecated
public AbstractMessageParser(
final SessionInputBuffer buffer,
final LineParser parser,
final HttpParams params) {
super();
Args.notNull(buffer, "Session input buffer");
Args.notNull(params, "HTTP parameters");
this.sessionBuffer = buffer;
this.messageConstraints = HttpParamConfig.getMessageConstraints(params);
this.lineParser = (parser != null) ? parser : BasicLineParser.INSTANCE;
this.headerLines = new ArrayList<CharArrayBuffer>();
this.state = HEAD_LINE;
}
/**
* Creates new instance of AbstractMessageParser.
*
* @param buffer the session input buffer.
* @param lineParser the line parser. If <code>null</code> {@link BasicLineParser#INSTANCE}
* will be used.
* @param constraints the message constraints. If <code>null</code>
* {@link MessageConstraints#DEFAULT} will be used.
*
* @since 4.3
*/
public AbstractMessageParser(
final SessionInputBuffer buffer,
final LineParser lineParser,
final MessageConstraints constraints) {
super();
this.sessionBuffer = Args.notNull(buffer, "Session input buffer");
this.lineParser = lineParser != null ? lineParser : BasicLineParser.INSTANCE;
this.messageConstraints = constraints != null ? constraints : MessageConstraints.DEFAULT;
this.headerLines = new ArrayList<CharArrayBuffer>();
this.state = HEAD_LINE;
}
/**
* Parses HTTP headers from the data receiver stream according to the generic
* format as given in Section 3.1 of RFC 822, RFC-2616 Section 4 and 19.3.
*
* @param inbuffer Session input buffer
* @param maxHeaderCount maximum number of headers allowed. If the number
* of headers received from the data stream exceeds maxCount value, an
* IOException will be thrown. Setting this parameter to a negative value
* or zero will disable the check.
* @param maxLineLen maximum number of characters for a header line,
* including the continuation lines. Setting this parameter to a negative
* value or zero will disable the check.
* @return array of HTTP headers
* @param parser line parser to use. Can be <code>null</code>, in which case
* the default implementation of this interface will be used.
*
* @throws IOException in case of an I/O error
* @throws HttpException in case of HTTP protocol violation
*/
public static Header[] parseHeaders(
final SessionInputBuffer inbuffer,
final int maxHeaderCount,
final int maxLineLen,
final LineParser parser) throws HttpException, IOException {
final List<CharArrayBuffer> headerLines = new ArrayList<CharArrayBuffer>();
return parseHeaders(inbuffer, maxHeaderCount, maxLineLen,
parser != null ? parser : BasicLineParser.INSTANCE,
headerLines);
}
/**
* Parses HTTP headers from the data receiver stream according to the generic
* format as given in Section 3.1 of RFC 822, RFC-2616 Section 4 and 19.3.
*
* @param inbuffer Session input buffer
* @param maxHeaderCount maximum number of headers allowed. If the number
* of headers received from the data stream exceeds maxCount value, an
* IOException will be thrown. Setting this parameter to a negative value
* or zero will disable the check.
* @param maxLineLen maximum number of characters for a header line,
* including the continuation lines. Setting this parameter to a negative
* value or zero will disable the check.
* @param parser line parser to use.
* @param headerLines List of header lines. This list will be used to store
* intermediate results. This makes it possible to resume parsing of
* headers in case of a {@link java.io.InterruptedIOException}.
*
* @return array of HTTP headers
*
* @throws IOException in case of an I/O error
* @throws HttpException in case of HTTP protocol violation
*
* @since 4.1
*/
public static Header[] parseHeaders(
final SessionInputBuffer inbuffer,
final int maxHeaderCount,
final int maxLineLen,
final LineParser parser,
final List<CharArrayBuffer> headerLines) throws HttpException, IOException {
Args.notNull(inbuffer, "Session input buffer");
Args.notNull(parser, "Line parser");
Args.notNull(headerLines, "Header line list");
CharArrayBuffer current = null;
CharArrayBuffer previous = null;
for (;;) {
if (current == null) {
current = new CharArrayBuffer(64);
} else {
current.clear();
}
final int l = inbuffer.readLine(current);
if (l == -1 || current.length() < 1) {
break;
}
// Parse the header name and value
// Check for folded headers first
// Detect LWS-char see HTTP/1.0 or HTTP/1.1 Section 2.2
// discussion on folded headers
if ((current.charAt(0) == ' ' || current.charAt(0) == '\t') && previous != null) {
// we have continuation folded header
// so append value
int i = 0;
while (i < current.length()) {
final char ch = current.charAt(i);
if (ch != ' ' && ch != '\t') {
break;
}
i++;
}
if (maxLineLen > 0
&& previous.length() + 1 + current.length() - i > maxLineLen) {
throw new MessageConstraintException("Maximum line length limit exceeded");
}
previous.append(' ');
previous.append(current, i, current.length() - i);
} else {
headerLines.add(current);
previous = current;
current = null;
}
if (maxHeaderCount > 0 && headerLines.size() >= maxHeaderCount) {
throw new MessageConstraintException("Maximum header count exceeded");
}
}
final Header[] headers = new Header[headerLines.size()];
for (int i = 0; i < headerLines.size(); i++) {
final CharArrayBuffer buffer = headerLines.get(i);
try {
headers[i] = parser.parseHeader(buffer);
} catch (final ParseException ex) {
throw new ProtocolException(ex.getMessage());
}
}
return headers;
}
/**
* Subclasses must override this method to generate an instance of
* {@link HttpMessage} based on the initial input from the session buffer.
* <p>
* Usually this method is expected to read just the very first line or
* the very first valid from the data stream and based on the input generate
* an appropriate instance of {@link HttpMessage}.
*
* @param sessionBuffer the session input buffer.
* @return HTTP message based on the input from the session buffer.
* @throws IOException in case of an I/O error.
* @throws HttpException in case of HTTP protocol violation.
* @throws ParseException in case of a parse error.
*/
protected abstract T parseHead(SessionInputBuffer sessionBuffer)
throws IOException, HttpException, ParseException;
public T parse() throws IOException, HttpException {
final int st = this.state;
switch (st) {
case HEAD_LINE:
try {
this.message = parseHead(this.sessionBuffer);
} catch (final ParseException px) {
throw new ProtocolException(px.getMessage(), px);
}
this.state = HEADERS;
//$FALL-THROUGH$
case HEADERS:
final Header[] headers = AbstractMessageParser.parseHeaders(
this.sessionBuffer,
this.messageConstraints.getMaxHeaderCount(),
this.messageConstraints.getMaxLineLength(),
this.lineParser,
this.headerLines);
this.message.setHeaders(headers);
final T result = this.message;
this.message = null;
this.headerLines.clear();
this.state = HEAD_LINE;
return result;
default:
throw new IllegalStateException("Inconsistent parser state");
}
}
}

View file

@ -0,0 +1,119 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HeaderIterator;
import ch.boye.httpclientandroidlib.HttpException;
import ch.boye.httpclientandroidlib.HttpMessage;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.io.HttpMessageWriter;
import ch.boye.httpclientandroidlib.io.SessionOutputBuffer;
import ch.boye.httpclientandroidlib.message.BasicLineFormatter;
import ch.boye.httpclientandroidlib.message.LineFormatter;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* Abstract base class for HTTP message writers that serialize output to
* an instance of {@link SessionOutputBuffer}.
*
* @since 4.0
*/
@SuppressWarnings("deprecation")
@NotThreadSafe
public abstract class AbstractMessageWriter<T extends HttpMessage> implements HttpMessageWriter<T> {
protected final SessionOutputBuffer sessionBuffer;
protected final CharArrayBuffer lineBuf;
protected final LineFormatter lineFormatter;
/**
* Creates an instance of AbstractMessageWriter.
*
* @param buffer the session output buffer.
* @param formatter the line formatter.
* @param params HTTP parameters.
*
* @deprecated (4.3) use
* {@link AbstractMessageWriter#AbstractMessageWriter(SessionOutputBuffer, LineFormatter)}
*/
@Deprecated
public AbstractMessageWriter(final SessionOutputBuffer buffer,
final LineFormatter formatter,
final HttpParams params) {
super();
Args.notNull(buffer, "Session input buffer");
this.sessionBuffer = buffer;
this.lineBuf = new CharArrayBuffer(128);
this.lineFormatter = (formatter != null) ? formatter : BasicLineFormatter.INSTANCE;
}
/**
* Creates an instance of AbstractMessageWriter.
*
* @param buffer the session output buffer.
* @param formatter the line formatter If <code>null</code> {@link BasicLineFormatter#INSTANCE}
* will be used.
*
* @since 4.3
*/
public AbstractMessageWriter(
final SessionOutputBuffer buffer,
final LineFormatter formatter) {
super();
this.sessionBuffer = Args.notNull(buffer, "Session input buffer");
this.lineFormatter = (formatter != null) ? formatter : BasicLineFormatter.INSTANCE;
this.lineBuf = new CharArrayBuffer(128);
}
/**
* Subclasses must override this method to write out the first header line
* based on the {@link HttpMessage} passed as a parameter.
*
* @param message the message whose first line is to be written out.
* @throws IOException in case of an I/O error.
*/
protected abstract void writeHeadLine(T message) throws IOException;
public void write(final T message) throws IOException, HttpException {
Args.notNull(message, "HTTP message");
writeHeadLine(message);
for (final HeaderIterator it = message.headerIterator(); it.hasNext(); ) {
final Header header = it.nextHeader();
this.sessionBuffer.writeLine
(lineFormatter.formatHeader(this.lineBuf, header));
}
this.lineBuf.clear();
this.sessionBuffer.writeLine(this.lineBuf);
}
}

View file

@ -0,0 +1,401 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import ch.boye.httpclientandroidlib.Consts;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.io.BufferInfo;
import ch.boye.httpclientandroidlib.io.HttpTransportMetrics;
import ch.boye.httpclientandroidlib.io.SessionInputBuffer;
import ch.boye.httpclientandroidlib.params.CoreConnectionPNames;
import ch.boye.httpclientandroidlib.params.CoreProtocolPNames;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.protocol.HTTP;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.ByteArrayBuffer;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* Abstract base class for session input buffers that stream data from
* an arbitrary {@link InputStream}. This class buffers input data in
* an internal byte array for optimal input performance.
* <p>
* {@link #readLine(CharArrayBuffer)} and {@link #readLine()} methods of this
* class treat a lone LF as valid line delimiters in addition to CR-LF required
* by the HTTP specification.
*
* @since 4.0
*
* @deprecated (4.3) use {@link SessionInputBufferImpl}
*/
@NotThreadSafe
@Deprecated
public abstract class AbstractSessionInputBuffer implements SessionInputBuffer, BufferInfo {
private InputStream instream;
private byte[] buffer;
private ByteArrayBuffer linebuffer;
private Charset charset;
private boolean ascii;
private int maxLineLen;
private int minChunkLimit;
private HttpTransportMetricsImpl metrics;
private CodingErrorAction onMalformedCharAction;
private CodingErrorAction onUnmappableCharAction;
private int bufferpos;
private int bufferlen;
private CharsetDecoder decoder;
private CharBuffer cbuf;
public AbstractSessionInputBuffer() {
}
/**
* Initializes this session input buffer.
*
* @param instream the source input stream.
* @param buffersize the size of the internal buffer.
* @param params HTTP parameters.
*/
protected void init(final InputStream instream, final int buffersize, final HttpParams params) {
Args.notNull(instream, "Input stream");
Args.notNegative(buffersize, "Buffer size");
Args.notNull(params, "HTTP parameters");
this.instream = instream;
this.buffer = new byte[buffersize];
this.bufferpos = 0;
this.bufferlen = 0;
this.linebuffer = new ByteArrayBuffer(buffersize);
final String charset = (String) params.getParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET);
this.charset = charset != null ? Charset.forName(charset) : Consts.ASCII;
this.ascii = this.charset.equals(Consts.ASCII);
this.decoder = null;
this.maxLineLen = params.getIntParameter(CoreConnectionPNames.MAX_LINE_LENGTH, -1);
this.minChunkLimit = params.getIntParameter(CoreConnectionPNames.MIN_CHUNK_LIMIT, 512);
this.metrics = createTransportMetrics();
final CodingErrorAction a1 = (CodingErrorAction) params.getParameter(
CoreProtocolPNames.HTTP_MALFORMED_INPUT_ACTION);
this.onMalformedCharAction = a1 != null ? a1 : CodingErrorAction.REPORT;
final CodingErrorAction a2 = (CodingErrorAction) params.getParameter(
CoreProtocolPNames.HTTP_UNMAPPABLE_INPUT_ACTION);
this.onUnmappableCharAction = a2 != null? a2 : CodingErrorAction.REPORT;
}
/**
* @since 4.1
*/
protected HttpTransportMetricsImpl createTransportMetrics() {
return new HttpTransportMetricsImpl();
}
/**
* @since 4.1
*/
public int capacity() {
return this.buffer.length;
}
/**
* @since 4.1
*/
public int length() {
return this.bufferlen - this.bufferpos;
}
/**
* @since 4.1
*/
public int available() {
return capacity() - length();
}
protected int fillBuffer() throws IOException {
// compact the buffer if necessary
if (this.bufferpos > 0) {
final int len = this.bufferlen - this.bufferpos;
if (len > 0) {
System.arraycopy(this.buffer, this.bufferpos, this.buffer, 0, len);
}
this.bufferpos = 0;
this.bufferlen = len;
}
final int l;
final int off = this.bufferlen;
final int len = this.buffer.length - off;
l = this.instream.read(this.buffer, off, len);
if (l == -1) {
return -1;
} else {
this.bufferlen = off + l;
this.metrics.incrementBytesTransferred(l);
return l;
}
}
protected boolean hasBufferedData() {
return this.bufferpos < this.bufferlen;
}
public int read() throws IOException {
int noRead;
while (!hasBufferedData()) {
noRead = fillBuffer();
if (noRead == -1) {
return -1;
}
}
return this.buffer[this.bufferpos++] & 0xff;
}
public int read(final byte[] b, final int off, final int len) throws IOException {
if (b == null) {
return 0;
}
if (hasBufferedData()) {
final int chunk = Math.min(len, this.bufferlen - this.bufferpos);
System.arraycopy(this.buffer, this.bufferpos, b, off, chunk);
this.bufferpos += chunk;
return chunk;
}
// If the remaining capacity is big enough, read directly from the
// underlying input stream bypassing the buffer.
if (len > this.minChunkLimit) {
final int read = this.instream.read(b, off, len);
if (read > 0) {
this.metrics.incrementBytesTransferred(read);
}
return read;
} else {
// otherwise read to the buffer first
while (!hasBufferedData()) {
final int noRead = fillBuffer();
if (noRead == -1) {
return -1;
}
}
final int chunk = Math.min(len, this.bufferlen - this.bufferpos);
System.arraycopy(this.buffer, this.bufferpos, b, off, chunk);
this.bufferpos += chunk;
return chunk;
}
}
public int read(final byte[] b) throws IOException {
if (b == null) {
return 0;
}
return read(b, 0, b.length);
}
private int locateLF() {
for (int i = this.bufferpos; i < this.bufferlen; i++) {
if (this.buffer[i] == HTTP.LF) {
return i;
}
}
return -1;
}
/**
* Reads a complete line of characters up to a line delimiter from this
* session buffer into the given line buffer. The number of chars actually
* read is returned as an integer. The line delimiter itself is discarded.
* If no char is available because the end of the stream has been reached,
* the value <code>-1</code> is returned. This method blocks until input
* data is available, end of file is detected, or an exception is thrown.
* <p>
* This method treats a lone LF as a valid line delimiters in addition
* to CR-LF required by the HTTP specification.
*
* @param charbuffer the line buffer.
* @return one line of characters
* @exception IOException if an I/O error occurs.
*/
public int readLine(final CharArrayBuffer charbuffer) throws IOException {
Args.notNull(charbuffer, "Char array buffer");
int noRead = 0;
boolean retry = true;
while (retry) {
// attempt to find end of line (LF)
final int i = locateLF();
if (i != -1) {
// end of line found.
if (this.linebuffer.isEmpty()) {
// the entire line is preset in the read buffer
return lineFromReadBuffer(charbuffer, i);
}
retry = false;
final int len = i + 1 - this.bufferpos;
this.linebuffer.append(this.buffer, this.bufferpos, len);
this.bufferpos = i + 1;
} else {
// end of line not found
if (hasBufferedData()) {
final int len = this.bufferlen - this.bufferpos;
this.linebuffer.append(this.buffer, this.bufferpos, len);
this.bufferpos = this.bufferlen;
}
noRead = fillBuffer();
if (noRead == -1) {
retry = false;
}
}
if (this.maxLineLen > 0 && this.linebuffer.length() >= this.maxLineLen) {
throw new IOException("Maximum line length limit exceeded");
}
}
if (noRead == -1 && this.linebuffer.isEmpty()) {
// indicate the end of stream
return -1;
}
return lineFromLineBuffer(charbuffer);
}
/**
* Reads a complete line of characters up to a line delimiter from this
* session buffer. The line delimiter itself is discarded. If no char is
* available because the end of the stream has been reached,
* <code>null</code> is returned. This method blocks until input data is
* available, end of file is detected, or an exception is thrown.
* <p>
* This method treats a lone LF as a valid line delimiters in addition
* to CR-LF required by the HTTP specification.
*
* @return HTTP line as a string
* @exception IOException if an I/O error occurs.
*/
private int lineFromLineBuffer(final CharArrayBuffer charbuffer)
throws IOException {
// discard LF if found
int len = this.linebuffer.length();
if (len > 0) {
if (this.linebuffer.byteAt(len - 1) == HTTP.LF) {
len--;
}
// discard CR if found
if (len > 0) {
if (this.linebuffer.byteAt(len - 1) == HTTP.CR) {
len--;
}
}
}
if (this.ascii) {
charbuffer.append(this.linebuffer, 0, len);
} else {
final ByteBuffer bbuf = ByteBuffer.wrap(this.linebuffer.buffer(), 0, len);
len = appendDecoded(charbuffer, bbuf);
}
this.linebuffer.clear();
return len;
}
private int lineFromReadBuffer(final CharArrayBuffer charbuffer, final int position)
throws IOException {
final int off = this.bufferpos;
int i = position;
this.bufferpos = i + 1;
if (i > off && this.buffer[i - 1] == HTTP.CR) {
// skip CR if found
i--;
}
int len = i - off;
if (this.ascii) {
charbuffer.append(this.buffer, off, len);
} else {
final ByteBuffer bbuf = ByteBuffer.wrap(this.buffer, off, len);
len = appendDecoded(charbuffer, bbuf);
}
return len;
}
private int appendDecoded(
final CharArrayBuffer charbuffer, final ByteBuffer bbuf) throws IOException {
if (!bbuf.hasRemaining()) {
return 0;
}
if (this.decoder == null) {
this.decoder = this.charset.newDecoder();
this.decoder.onMalformedInput(this.onMalformedCharAction);
this.decoder.onUnmappableCharacter(this.onUnmappableCharAction);
}
if (this.cbuf == null) {
this.cbuf = CharBuffer.allocate(1024);
}
this.decoder.reset();
int len = 0;
while (bbuf.hasRemaining()) {
final CoderResult result = this.decoder.decode(bbuf, this.cbuf, true);
len += handleDecodingResult(result, charbuffer, bbuf);
}
final CoderResult result = this.decoder.flush(this.cbuf);
len += handleDecodingResult(result, charbuffer, bbuf);
this.cbuf.clear();
return len;
}
private int handleDecodingResult(
final CoderResult result,
final CharArrayBuffer charbuffer,
final ByteBuffer bbuf) throws IOException {
if (result.isError()) {
result.throwException();
}
this.cbuf.flip();
final int len = this.cbuf.remaining();
while (this.cbuf.hasRemaining()) {
charbuffer.append(this.cbuf.get());
}
this.cbuf.compact();
return len;
}
public String readLine() throws IOException {
final CharArrayBuffer charbuffer = new CharArrayBuffer(64);
final int l = readLine(charbuffer);
if (l != -1) {
return charbuffer.toString();
} else {
return null;
}
}
public HttpTransportMetrics getMetrics() {
return this.metrics;
}
}

View file

@ -0,0 +1,307 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import ch.boye.httpclientandroidlib.Consts;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.io.BufferInfo;
import ch.boye.httpclientandroidlib.io.HttpTransportMetrics;
import ch.boye.httpclientandroidlib.io.SessionOutputBuffer;
import ch.boye.httpclientandroidlib.params.CoreConnectionPNames;
import ch.boye.httpclientandroidlib.params.CoreProtocolPNames;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.protocol.HTTP;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.ByteArrayBuffer;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* Abstract base class for session output buffers that stream data to
* an arbitrary {@link OutputStream}. This class buffers small chunks of
* output data in an internal byte array for optimal output performance.
* </p>
* {@link #writeLine(CharArrayBuffer)} and {@link #writeLine(String)} methods
* of this class use CR-LF as a line delimiter.
*
* @since 4.0
*
* @deprecated (4.3) use {@link SessionOutputBufferImpl}
*/
@NotThreadSafe
@Deprecated
public abstract class AbstractSessionOutputBuffer implements SessionOutputBuffer, BufferInfo {
private static final byte[] CRLF = new byte[] {HTTP.CR, HTTP.LF};
private OutputStream outstream;
private ByteArrayBuffer buffer;
private Charset charset;
private boolean ascii;
private int minChunkLimit;
private HttpTransportMetricsImpl metrics;
private CodingErrorAction onMalformedCharAction;
private CodingErrorAction onUnmappableCharAction;
private CharsetEncoder encoder;
private ByteBuffer bbuf;
protected AbstractSessionOutputBuffer(
final OutputStream outstream,
final int buffersize,
final Charset charset,
final int minChunkLimit,
final CodingErrorAction malformedCharAction,
final CodingErrorAction unmappableCharAction) {
super();
Args.notNull(outstream, "Input stream");
Args.notNegative(buffersize, "Buffer size");
this.outstream = outstream;
this.buffer = new ByteArrayBuffer(buffersize);
this.charset = charset != null ? charset : Consts.ASCII;
this.ascii = this.charset.equals(Consts.ASCII);
this.encoder = null;
this.minChunkLimit = minChunkLimit >= 0 ? minChunkLimit : 512;
this.metrics = createTransportMetrics();
this.onMalformedCharAction = malformedCharAction != null ? malformedCharAction :
CodingErrorAction.REPORT;
this.onUnmappableCharAction = unmappableCharAction != null? unmappableCharAction :
CodingErrorAction.REPORT;
}
public AbstractSessionOutputBuffer() {
}
protected void init(final OutputStream outstream, final int buffersize, final HttpParams params) {
Args.notNull(outstream, "Input stream");
Args.notNegative(buffersize, "Buffer size");
Args.notNull(params, "HTTP parameters");
this.outstream = outstream;
this.buffer = new ByteArrayBuffer(buffersize);
final String charset = (String) params.getParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET);
this.charset = charset != null ? Charset.forName(charset) : Consts.ASCII;
this.ascii = this.charset.equals(Consts.ASCII);
this.encoder = null;
this.minChunkLimit = params.getIntParameter(CoreConnectionPNames.MIN_CHUNK_LIMIT, 512);
this.metrics = createTransportMetrics();
final CodingErrorAction a1 = (CodingErrorAction) params.getParameter(
CoreProtocolPNames.HTTP_MALFORMED_INPUT_ACTION);
this.onMalformedCharAction = a1 != null ? a1 : CodingErrorAction.REPORT;
final CodingErrorAction a2 = (CodingErrorAction) params.getParameter(
CoreProtocolPNames.HTTP_UNMAPPABLE_INPUT_ACTION);
this.onUnmappableCharAction = a2 != null? a2 : CodingErrorAction.REPORT;
}
/**
* @since 4.1
*/
protected HttpTransportMetricsImpl createTransportMetrics() {
return new HttpTransportMetricsImpl();
}
/**
* @since 4.1
*/
public int capacity() {
return this.buffer.capacity();
}
/**
* @since 4.1
*/
public int length() {
return this.buffer.length();
}
/**
* @since 4.1
*/
public int available() {
return capacity() - length();
}
protected void flushBuffer() throws IOException {
final int len = this.buffer.length();
if (len > 0) {
this.outstream.write(this.buffer.buffer(), 0, len);
this.buffer.clear();
this.metrics.incrementBytesTransferred(len);
}
}
public void flush() throws IOException {
flushBuffer();
this.outstream.flush();
}
public void write(final byte[] b, final int off, final int len) throws IOException {
if (b == null) {
return;
}
// Do not want to buffer large-ish chunks
// if the byte array is larger then MIN_CHUNK_LIMIT
// write it directly to the output stream
if (len > this.minChunkLimit || len > this.buffer.capacity()) {
// flush the buffer
flushBuffer();
// write directly to the out stream
this.outstream.write(b, off, len);
this.metrics.incrementBytesTransferred(len);
} else {
// Do not let the buffer grow unnecessarily
final int freecapacity = this.buffer.capacity() - this.buffer.length();
if (len > freecapacity) {
// flush the buffer
flushBuffer();
}
// buffer
this.buffer.append(b, off, len);
}
}
public void write(final byte[] b) throws IOException {
if (b == null) {
return;
}
write(b, 0, b.length);
}
public void write(final int b) throws IOException {
if (this.buffer.isFull()) {
flushBuffer();
}
this.buffer.append(b);
}
/**
* Writes characters from the specified string followed by a line delimiter
* to this session buffer.
* <p>
* This method uses CR-LF as a line delimiter.
*
* @param s the line.
* @exception IOException if an I/O error occurs.
*/
public void writeLine(final String s) throws IOException {
if (s == null) {
return;
}
if (s.length() > 0) {
if (this.ascii) {
for (int i = 0; i < s.length(); i++) {
write(s.charAt(i));
}
} else {
final CharBuffer cbuf = CharBuffer.wrap(s);
writeEncoded(cbuf);
}
}
write(CRLF);
}
/**
* Writes characters from the specified char array followed by a line
* delimiter to this session buffer.
* <p>
* This method uses CR-LF as a line delimiter.
*
* @param charbuffer the buffer containing chars of the line.
* @exception IOException if an I/O error occurs.
*/
public void writeLine(final CharArrayBuffer charbuffer) throws IOException {
if (charbuffer == null) {
return;
}
if (this.ascii) {
int off = 0;
int remaining = charbuffer.length();
while (remaining > 0) {
int chunk = this.buffer.capacity() - this.buffer.length();
chunk = Math.min(chunk, remaining);
if (chunk > 0) {
this.buffer.append(charbuffer, off, chunk);
}
if (this.buffer.isFull()) {
flushBuffer();
}
off += chunk;
remaining -= chunk;
}
} else {
final CharBuffer cbuf = CharBuffer.wrap(charbuffer.buffer(), 0, charbuffer.length());
writeEncoded(cbuf);
}
write(CRLF);
}
private void writeEncoded(final CharBuffer cbuf) throws IOException {
if (!cbuf.hasRemaining()) {
return;
}
if (this.encoder == null) {
this.encoder = this.charset.newEncoder();
this.encoder.onMalformedInput(this.onMalformedCharAction);
this.encoder.onUnmappableCharacter(this.onUnmappableCharAction);
}
if (this.bbuf == null) {
this.bbuf = ByteBuffer.allocate(1024);
}
this.encoder.reset();
while (cbuf.hasRemaining()) {
final CoderResult result = this.encoder.encode(cbuf, this.bbuf, true);
handleEncodingResult(result);
}
final CoderResult result = this.encoder.flush(this.bbuf);
handleEncodingResult(result);
this.bbuf.clear();
}
private void handleEncodingResult(final CoderResult result) throws IOException {
if (result.isError()) {
result.throwException();
}
this.bbuf.flip();
while (this.bbuf.hasRemaining()) {
write(this.bbuf.get());
}
this.bbuf.compact();
}
public HttpTransportMetrics getMetrics() {
return this.metrics;
}
}

View file

@ -0,0 +1,301 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import java.io.InputStream;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpException;
import ch.boye.httpclientandroidlib.MalformedChunkCodingException;
import ch.boye.httpclientandroidlib.TruncatedChunkException;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.io.BufferInfo;
import ch.boye.httpclientandroidlib.io.SessionInputBuffer;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* Implements chunked transfer coding. The content is received in small chunks.
* Entities transferred using this input stream can be of unlimited length.
* After the stream is read to the end, it provides access to the trailers,
* if any.
* <p>
* Note that this class NEVER closes the underlying stream, even when close
* gets called. Instead, it will read until the "end" of its chunking on
* close, which allows for the seamless execution of subsequent HTTP 1.1
* requests, while not requiring the client to remember to read the entire
* contents of the response.
*
*
* @since 4.0
*
*/
@NotThreadSafe
public class ChunkedInputStream extends InputStream {
private static final int CHUNK_LEN = 1;
private static final int CHUNK_DATA = 2;
private static final int CHUNK_CRLF = 3;
private static final int BUFFER_SIZE = 2048;
/** The session input buffer */
private final SessionInputBuffer in;
private final CharArrayBuffer buffer;
private int state;
/** The chunk size */
private int chunkSize;
/** The current position within the current chunk */
private int pos;
/** True if we've reached the end of stream */
private boolean eof = false;
/** True if this stream is closed */
private boolean closed = false;
private Header[] footers = new Header[] {};
/**
* Wraps session input stream and reads chunk coded input.
*
* @param in The session input buffer
*/
public ChunkedInputStream(final SessionInputBuffer in) {
super();
this.in = Args.notNull(in, "Session input buffer");
this.pos = 0;
this.buffer = new CharArrayBuffer(16);
this.state = CHUNK_LEN;
}
@Override
public int available() throws IOException {
if (this.in instanceof BufferInfo) {
final int len = ((BufferInfo) this.in).length();
return Math.min(len, this.chunkSize - this.pos);
} else {
return 0;
}
}
/**
* <p> Returns all the data in a chunked stream in coalesced form. A chunk
* is followed by a CRLF. The method returns -1 as soon as a chunksize of 0
* is detected.</p>
*
* <p> Trailer headers are read automatically at the end of the stream and
* can be obtained with the getResponseFooters() method.</p>
*
* @return -1 of the end of the stream has been reached or the next data
* byte
* @throws IOException in case of an I/O error
*/
@Override
public int read() throws IOException {
if (this.closed) {
throw new IOException("Attempted read from closed stream.");
}
if (this.eof) {
return -1;
}
if (state != CHUNK_DATA) {
nextChunk();
if (this.eof) {
return -1;
}
}
final int b = in.read();
if (b != -1) {
pos++;
if (pos >= chunkSize) {
state = CHUNK_CRLF;
}
}
return b;
}
/**
* Read some bytes from the stream.
* @param b The byte array that will hold the contents from the stream.
* @param off The offset into the byte array at which bytes will start to be
* placed.
* @param len the maximum number of bytes that can be returned.
* @return The number of bytes returned or -1 if the end of stream has been
* reached.
* @throws IOException in case of an I/O error
*/
@Override
public int read (final byte[] b, final int off, final int len) throws IOException {
if (closed) {
throw new IOException("Attempted read from closed stream.");
}
if (eof) {
return -1;
}
if (state != CHUNK_DATA) {
nextChunk();
if (eof) {
return -1;
}
}
final int bytesRead = in.read(b, off, Math.min(len, chunkSize - pos));
if (bytesRead != -1) {
pos += bytesRead;
if (pos >= chunkSize) {
state = CHUNK_CRLF;
}
return bytesRead;
} else {
eof = true;
throw new TruncatedChunkException("Truncated chunk "
+ "( expected size: " + chunkSize
+ "; actual size: " + pos + ")");
}
}
/**
* Read some bytes from the stream.
* @param b The byte array that will hold the contents from the stream.
* @return The number of bytes returned or -1 if the end of stream has been
* reached.
* @throws IOException in case of an I/O error
*/
@Override
public int read (final byte[] b) throws IOException {
return read(b, 0, b.length);
}
/**
* Read the next chunk.
* @throws IOException in case of an I/O error
*/
private void nextChunk() throws IOException {
chunkSize = getChunkSize();
if (chunkSize < 0) {
throw new MalformedChunkCodingException("Negative chunk size");
}
state = CHUNK_DATA;
pos = 0;
if (chunkSize == 0) {
eof = true;
parseTrailerHeaders();
}
}
/**
* Expects the stream to start with a chunksize in hex with optional
* comments after a semicolon. The line must end with a CRLF: "a3; some
* comment\r\n" Positions the stream at the start of the next line.
*/
private int getChunkSize() throws IOException {
final int st = this.state;
switch (st) {
case CHUNK_CRLF:
this.buffer.clear();
final int bytesRead1 = this.in.readLine(this.buffer);
if (bytesRead1 == -1) {
return 0;
}
if (!this.buffer.isEmpty()) {
throw new MalformedChunkCodingException(
"Unexpected content at the end of chunk");
}
state = CHUNK_LEN;
//$FALL-THROUGH$
case CHUNK_LEN:
this.buffer.clear();
final int bytesRead2 = this.in.readLine(this.buffer);
if (bytesRead2 == -1) {
return 0;
}
int separator = this.buffer.indexOf(';');
if (separator < 0) {
separator = this.buffer.length();
}
try {
return Integer.parseInt(this.buffer.substringTrimmed(0, separator), 16);
} catch (final NumberFormatException e) {
throw new MalformedChunkCodingException("Bad chunk header");
}
default:
throw new IllegalStateException("Inconsistent codec state");
}
}
/**
* Reads and stores the Trailer headers.
* @throws IOException in case of an I/O error
*/
private void parseTrailerHeaders() throws IOException {
try {
this.footers = AbstractMessageParser.parseHeaders
(in, -1, -1, null);
} catch (final HttpException ex) {
final IOException ioe = new MalformedChunkCodingException("Invalid footer: "
+ ex.getMessage());
ioe.initCause(ex);
throw ioe;
}
}
/**
* Upon close, this reads the remainder of the chunked message,
* leaving the underlying socket at a position to start reading the
* next response without scanning.
* @throws IOException in case of an I/O error
*/
@Override
public void close() throws IOException {
if (!closed) {
try {
if (!eof) {
// read and discard the remainder of the message
final byte buff[] = new byte[BUFFER_SIZE];
while (read(buff) >= 0) {
}
}
} finally {
eof = true;
closed = true;
}
}
}
public Header[] getFooters() {
return this.footers.clone();
}
}

View file

@ -0,0 +1,208 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import java.io.OutputStream;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.io.SessionOutputBuffer;
/**
* Implements chunked transfer coding. The content is sent in small chunks.
* Entities transferred using this output stream can be of unlimited length.
* Writes are buffered to an internal buffer (2048 default size).
* <p>
* Note that this class NEVER closes the underlying stream, even when close
* gets called. Instead, the stream will be marked as closed and no further
* output will be permitted.
*
*
* @since 4.0
*/
@NotThreadSafe
public class ChunkedOutputStream extends OutputStream {
// ----------------------------------------------------- Instance Variables
private final SessionOutputBuffer out;
private final byte[] cache;
private int cachePosition = 0;
private boolean wroteLastChunk = false;
/** True if the stream is closed. */
private boolean closed = false;
/**
* Wraps a session output buffer and chunk-encodes the output.
*
* @param out The session output buffer
* @param bufferSize The minimum chunk size (excluding last chunk)
* @throws IOException not thrown
*
* @deprecated (4.3) use {@link ChunkedOutputStream#ChunkedOutputStream(int, SessionOutputBuffer)}
*/
@Deprecated
public ChunkedOutputStream(final SessionOutputBuffer out, final int bufferSize)
throws IOException {
this(bufferSize, out);
}
/**
* Wraps a session output buffer and chunks the output. The default buffer
* size of 2048 was chosen because the chunk overhead is less than 0.5%
*
* @param out the output buffer to wrap
* @throws IOException not thrown
*
* @deprecated (4.3) use {@link ChunkedOutputStream#ChunkedOutputStream(int, SessionOutputBuffer)}
*/
@Deprecated
public ChunkedOutputStream(final SessionOutputBuffer out)
throws IOException {
this(2048, out);
}
/**
* Wraps a session output buffer and chunk-encodes the output.
*
* @param bufferSize The minimum chunk size (excluding last chunk)
* @param out The session output buffer
*/
public ChunkedOutputStream(final int bufferSize, final SessionOutputBuffer out) {
super();
this.cache = new byte[bufferSize];
this.out = out;
}
/**
* Writes the cache out onto the underlying stream
*/
protected void flushCache() throws IOException {
if (this.cachePosition > 0) {
this.out.writeLine(Integer.toHexString(this.cachePosition));
this.out.write(this.cache, 0, this.cachePosition);
this.out.writeLine("");
this.cachePosition = 0;
}
}
/**
* Writes the cache and bufferToAppend to the underlying stream
* as one large chunk
*/
protected void flushCacheWithAppend(final byte bufferToAppend[], final int off, final int len) throws IOException {
this.out.writeLine(Integer.toHexString(this.cachePosition + len));
this.out.write(this.cache, 0, this.cachePosition);
this.out.write(bufferToAppend, off, len);
this.out.writeLine("");
this.cachePosition = 0;
}
protected void writeClosingChunk() throws IOException {
// Write the final chunk.
this.out.writeLine("0");
this.out.writeLine("");
}
// ----------------------------------------------------------- Public Methods
/**
* Must be called to ensure the internal cache is flushed and the closing
* chunk is written.
* @throws IOException in case of an I/O error
*/
public void finish() throws IOException {
if (!this.wroteLastChunk) {
flushCache();
writeClosingChunk();
this.wroteLastChunk = true;
}
}
// -------------------------------------------- OutputStream Methods
@Override
public void write(final int b) throws IOException {
if (this.closed) {
throw new IOException("Attempted write to closed stream.");
}
this.cache[this.cachePosition] = (byte) b;
this.cachePosition++;
if (this.cachePosition == this.cache.length) {
flushCache();
}
}
/**
* Writes the array. If the array does not fit within the buffer, it is
* not split, but rather written out as one large chunk.
*/
@Override
public void write(final byte b[]) throws IOException {
write(b, 0, b.length);
}
/**
* Writes the array. If the array does not fit within the buffer, it is
* not split, but rather written out as one large chunk.
*/
@Override
public void write(final byte src[], final int off, final int len) throws IOException {
if (this.closed) {
throw new IOException("Attempted write to closed stream.");
}
if (len >= this.cache.length - this.cachePosition) {
flushCacheWithAppend(src, off, len);
} else {
System.arraycopy(src, off, cache, this.cachePosition, len);
this.cachePosition += len;
}
}
/**
* Flushes the content buffer and the underlying stream.
*/
@Override
public void flush() throws IOException {
flushCache();
this.out.flush();
}
/**
* Finishes writing to the underlying stream, but does NOT close the underlying stream.
*/
@Override
public void close() throws IOException {
if (!this.closed) {
this.closed = true;
finish();
this.out.flush();
}
}
}

View file

@ -0,0 +1,232 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import java.io.InputStream;
import ch.boye.httpclientandroidlib.ConnectionClosedException;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.io.BufferInfo;
import ch.boye.httpclientandroidlib.io.SessionInputBuffer;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Input stream that cuts off after a defined number of bytes. This class
* is used to receive content of HTTP messages where the end of the content
* entity is determined by the value of the <code>Content-Length header</code>.
* Entities transferred using this stream can be maximum {@link Long#MAX_VALUE}
* long.
* <p>
* Note that this class NEVER closes the underlying stream, even when close
* gets called. Instead, it will read until the "end" of its limit on
* close, which allows for the seamless execution of subsequent HTTP 1.1
* requests, while not requiring the client to remember to read the entire
* contents of the response.
*
*
* @since 4.0
*/
@NotThreadSafe
public class ContentLengthInputStream extends InputStream {
private static final int BUFFER_SIZE = 2048;
/**
* The maximum number of bytes that can be read from the stream. Subsequent
* read operations will return -1.
*/
private final long contentLength;
/** The current position */
private long pos = 0;
/** True if the stream is closed. */
private boolean closed = false;
/**
* Wrapped input stream that all calls are delegated to.
*/
private SessionInputBuffer in = null;
/**
* Wraps a session input buffer and cuts off output after a defined number
* of bytes.
*
* @param in The session input buffer
* @param contentLength The maximum number of bytes that can be read from
* the stream. Subsequent read operations will return -1.
*/
public ContentLengthInputStream(final SessionInputBuffer in, final long contentLength) {
super();
this.in = Args.notNull(in, "Session input buffer");
this.contentLength = Args.notNegative(contentLength, "Content length");
}
/**
* <p>Reads until the end of the known length of content.</p>
*
* <p>Does not close the underlying socket input, but instead leaves it
* primed to parse the next response.</p>
* @throws IOException If an IO problem occurs.
*/
@Override
public void close() throws IOException {
if (!closed) {
try {
if (pos < contentLength) {
final byte buffer[] = new byte[BUFFER_SIZE];
while (read(buffer) >= 0) {
}
}
} finally {
// close after above so that we don't throw an exception trying
// to read after closed!
closed = true;
}
}
}
@Override
public int available() throws IOException {
if (this.in instanceof BufferInfo) {
final int len = ((BufferInfo) this.in).length();
return Math.min(len, (int) (this.contentLength - this.pos));
} else {
return 0;
}
}
/**
* Read the next byte from the stream
* @return The next byte or -1 if the end of stream has been reached.
* @throws IOException If an IO problem occurs
* @see java.io.InputStream#read()
*/
@Override
public int read() throws IOException {
if (closed) {
throw new IOException("Attempted read from closed stream.");
}
if (pos >= contentLength) {
return -1;
}
final int b = this.in.read();
if (b == -1) {
if (pos < contentLength) {
throw new ConnectionClosedException(
"Premature end of Content-Length delimited message body (expected: "
+ contentLength + "; received: " + pos);
}
} else {
pos++;
}
return b;
}
/**
* Does standard {@link InputStream#read(byte[], int, int)} behavior, but
* also notifies the watcher when the contents have been consumed.
*
* @param b The byte array to fill.
* @param off Start filling at this position.
* @param len The number of bytes to attempt to read.
* @return The number of bytes read, or -1 if the end of content has been
* reached.
*
* @throws java.io.IOException Should an error occur on the wrapped stream.
*/
@Override
public int read (final byte[] b, final int off, final int len) throws java.io.IOException {
if (closed) {
throw new IOException("Attempted read from closed stream.");
}
if (pos >= contentLength) {
return -1;
}
int chunk = len;
if (pos + len > contentLength) {
chunk = (int) (contentLength - pos);
}
final int count = this.in.read(b, off, chunk);
if (count == -1 && pos < contentLength) {
throw new ConnectionClosedException(
"Premature end of Content-Length delimited message body (expected: "
+ contentLength + "; received: " + pos);
}
if (count > 0) {
pos += count;
}
return count;
}
/**
* Read more bytes from the stream.
* @param b The byte array to put the new data in.
* @return The number of bytes read into the buffer.
* @throws IOException If an IO problem occurs
* @see java.io.InputStream#read(byte[])
*/
@Override
public int read(final byte[] b) throws IOException {
return read(b, 0, b.length);
}
/**
* Skips and discards a number of bytes from the input stream.
* @param n The number of bytes to skip.
* @return The actual number of bytes skipped. <= 0 if no bytes
* are skipped.
* @throws IOException If an error occurs while skipping bytes.
* @see InputStream#skip(long)
*/
@Override
public long skip(final long n) throws IOException {
if (n <= 0) {
return 0;
}
final byte[] buffer = new byte[BUFFER_SIZE];
// make sure we don't skip more bytes than are
// still available
long remaining = Math.min(n, this.contentLength - this.pos);
// skip and keep track of the bytes actually skipped
long count = 0;
while (remaining > 0) {
final int l = read(buffer, 0, (int)Math.min(BUFFER_SIZE, remaining));
if (l == -1) {
break;
}
count += l;
remaining -= l;
}
return count;
}
}

View file

@ -0,0 +1,136 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import java.io.OutputStream;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.io.SessionOutputBuffer;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Output stream that cuts off after a defined number of bytes. This class
* is used to send content of HTTP messages where the end of the content entity
* is determined by the value of the <code>Content-Length header</code>.
* Entities transferred using this stream can be maximum {@link Long#MAX_VALUE}
* long.
* <p>
* Note that this class NEVER closes the underlying stream, even when close
* gets called. Instead, the stream will be marked as closed and no further
* output will be permitted.
*
* @since 4.0
*/
@NotThreadSafe
public class ContentLengthOutputStream extends OutputStream {
/**
* Wrapped session output buffer.
*/
private final SessionOutputBuffer out;
/**
* The maximum number of bytes that can be written the stream. Subsequent
* write operations will be ignored.
*/
private final long contentLength;
/** Total bytes written */
private long total = 0;
/** True if the stream is closed. */
private boolean closed = false;
/**
* Wraps a session output buffer and cuts off output after a defined number
* of bytes.
*
* @param out The session output buffer
* @param contentLength The maximum number of bytes that can be written to
* the stream. Subsequent write operations will be ignored.
*
* @since 4.0
*/
public ContentLengthOutputStream(final SessionOutputBuffer out, final long contentLength) {
super();
this.out = Args.notNull(out, "Session output buffer");
this.contentLength = Args.notNegative(contentLength, "Content length");
}
/**
* <p>Does not close the underlying socket output.</p>
*
* @throws IOException If an I/O problem occurs.
*/
@Override
public void close() throws IOException {
if (!this.closed) {
this.closed = true;
this.out.flush();
}
}
@Override
public void flush() throws IOException {
this.out.flush();
}
@Override
public void write(final byte[] b, final int off, final int len) throws IOException {
if (this.closed) {
throw new IOException("Attempted write to closed stream.");
}
if (this.total < this.contentLength) {
final long max = this.contentLength - this.total;
int chunk = len;
if (chunk > max) {
chunk = (int) max;
}
this.out.write(b, off, chunk);
this.total += chunk;
}
}
@Override
public void write(final byte[] b) throws IOException {
write(b, 0, b.length);
}
@Override
public void write(final int b) throws IOException {
if (this.closed) {
throw new IOException("Attempted write to closed stream.");
}
if (this.total < this.contentLength) {
this.out.write(b);
this.total++;
}
}
}

View file

@ -0,0 +1,140 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import ch.boye.httpclientandroidlib.ConnectionClosedException;
import ch.boye.httpclientandroidlib.HttpException;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpRequestFactory;
import ch.boye.httpclientandroidlib.ParseException;
import ch.boye.httpclientandroidlib.RequestLine;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.config.MessageConstraints;
import ch.boye.httpclientandroidlib.impl.DefaultHttpRequestFactory;
import ch.boye.httpclientandroidlib.io.SessionInputBuffer;
import ch.boye.httpclientandroidlib.message.LineParser;
import ch.boye.httpclientandroidlib.message.ParserCursor;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* HTTP request parser that obtain its input from an instance
* of {@link SessionInputBuffer}.
*
* @since 4.2
*/
@SuppressWarnings("deprecation")
@NotThreadSafe
public class DefaultHttpRequestParser extends AbstractMessageParser<HttpRequest> {
private final HttpRequestFactory requestFactory;
private final CharArrayBuffer lineBuf;
/**
* Creates an instance of this class.
*
* @param buffer the session input buffer.
* @param lineParser the line parser.
* @param requestFactory the factory to use to create
* {@link HttpRequest}s.
* @param params HTTP parameters.
*
* @deprecated (4.3) use
* {@link DefaultHttpRequestParser#DefaultHttpRequestParser(SessionInputBuffer, LineParser,
* HttpRequestFactory, MessageConstraints)}
*/
@Deprecated
public DefaultHttpRequestParser(
final SessionInputBuffer buffer,
final LineParser lineParser,
final HttpRequestFactory requestFactory,
final HttpParams params) {
super(buffer, lineParser, params);
this.requestFactory = Args.notNull(requestFactory, "Request factory");
this.lineBuf = new CharArrayBuffer(128);
}
/**
* Creates new instance of DefaultHttpRequestParser.
*
* @param buffer the session input buffer.
* @param lineParser the line parser. If <code>null</code>
* {@link ch.boye.httpclientandroidlib.message.BasicLineParser#INSTANCE} will be used.
* @param requestFactory the response factory. If <code>null</code>
* {@link DefaultHttpRequestFactory#INSTANCE} will be used.
* @param constraints the message constraints. If <code>null</code>
* {@link MessageConstraints#DEFAULT} will be used.
*
* @since 4.3
*/
public DefaultHttpRequestParser(
final SessionInputBuffer buffer,
final LineParser lineParser,
final HttpRequestFactory requestFactory,
final MessageConstraints constraints) {
super(buffer, lineParser, constraints);
this.requestFactory = requestFactory != null ? requestFactory :
DefaultHttpRequestFactory.INSTANCE;
this.lineBuf = new CharArrayBuffer(128);
}
/**
* @since 4.3
*/
public DefaultHttpRequestParser(
final SessionInputBuffer buffer,
final MessageConstraints constraints) {
this(buffer, null, null, constraints);
}
/**
* @since 4.3
*/
public DefaultHttpRequestParser(final SessionInputBuffer buffer) {
this(buffer, null, null, MessageConstraints.DEFAULT);
}
@Override
protected HttpRequest parseHead(
final SessionInputBuffer sessionBuffer)
throws IOException, HttpException, ParseException {
this.lineBuf.clear();
final int i = sessionBuffer.readLine(this.lineBuf);
if (i == -1) {
throw new ConnectionClosedException("Client closed connection");
}
final ParserCursor cursor = new ParserCursor(0, this.lineBuf.length());
final RequestLine requestline = this.lineParser.parseRequestLine(this.lineBuf, cursor);
return this.requestFactory.newHttpRequest(requestline);
}
}

View file

@ -0,0 +1,71 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpRequestFactory;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.config.MessageConstraints;
import ch.boye.httpclientandroidlib.impl.DefaultHttpRequestFactory;
import ch.boye.httpclientandroidlib.io.HttpMessageParser;
import ch.boye.httpclientandroidlib.io.HttpMessageParserFactory;
import ch.boye.httpclientandroidlib.io.SessionInputBuffer;
import ch.boye.httpclientandroidlib.message.BasicLineParser;
import ch.boye.httpclientandroidlib.message.LineParser;
/**
* Default factory for request message parsers.
*
* @since 4.3
*/
@Immutable
public class DefaultHttpRequestParserFactory implements HttpMessageParserFactory<HttpRequest> {
public static final DefaultHttpRequestParserFactory INSTANCE = new DefaultHttpRequestParserFactory();
private final LineParser lineParser;
private final HttpRequestFactory requestFactory;
public DefaultHttpRequestParserFactory(final LineParser lineParser,
final HttpRequestFactory requestFactory) {
super();
this.lineParser = lineParser != null ? lineParser : BasicLineParser.INSTANCE;
this.requestFactory = requestFactory != null ? requestFactory
: DefaultHttpRequestFactory.INSTANCE;
}
public DefaultHttpRequestParserFactory() {
this(null, null);
}
public HttpMessageParser<HttpRequest> create(final SessionInputBuffer buffer,
final MessageConstraints constraints) {
return new DefaultHttpRequestParser(buffer, lineParser, requestFactory, constraints);
}
}

View file

@ -0,0 +1,69 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.io.SessionOutputBuffer;
import ch.boye.httpclientandroidlib.message.LineFormatter;
/**
* HTTP request writer that serializes its output to an instance of {@link SessionOutputBuffer}.
*
* @since 4.3
*/
@NotThreadSafe
public class DefaultHttpRequestWriter extends AbstractMessageWriter<HttpRequest> {
/**
* Creates an instance of DefaultHttpRequestWriter.
*
* @param buffer the session output buffer.
* @param formatter the line formatter If <code>null</code>
* {@link ch.boye.httpclientandroidlib.message.BasicLineFormatter#INSTANCE}
* will be used.
*/
public DefaultHttpRequestWriter(
final SessionOutputBuffer buffer,
final LineFormatter formatter) {
super(buffer, formatter);
}
public DefaultHttpRequestWriter(final SessionOutputBuffer buffer) {
this(buffer, null);
}
@Override
protected void writeHeadLine(final HttpRequest message) throws IOException {
lineFormatter.formatRequestLine(this.lineBuf, message.getRequestLine());
this.sessionBuffer.writeLine(this.lineBuf);
}
}

View file

@ -0,0 +1,63 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.io.HttpMessageWriter;
import ch.boye.httpclientandroidlib.io.HttpMessageWriterFactory;
import ch.boye.httpclientandroidlib.io.SessionOutputBuffer;
import ch.boye.httpclientandroidlib.message.BasicLineFormatter;
import ch.boye.httpclientandroidlib.message.LineFormatter;
/**
* Default factory for request message writers.
*
* @since 4.3
*/
@Immutable
public class DefaultHttpRequestWriterFactory implements HttpMessageWriterFactory<HttpRequest> {
public static final DefaultHttpRequestWriterFactory INSTANCE = new DefaultHttpRequestWriterFactory();
private final LineFormatter lineFormatter;
public DefaultHttpRequestWriterFactory(final LineFormatter lineFormatter) {
super();
this.lineFormatter = lineFormatter != null ? lineFormatter : BasicLineFormatter.INSTANCE;
}
public DefaultHttpRequestWriterFactory() {
this(null);
}
public HttpMessageWriter<HttpRequest> create(final SessionOutputBuffer buffer) {
return new DefaultHttpRequestWriter(buffer, lineFormatter);
}
}

View file

@ -0,0 +1,141 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import ch.boye.httpclientandroidlib.HttpException;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpResponseFactory;
import ch.boye.httpclientandroidlib.NoHttpResponseException;
import ch.boye.httpclientandroidlib.ParseException;
import ch.boye.httpclientandroidlib.StatusLine;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.config.MessageConstraints;
import ch.boye.httpclientandroidlib.impl.DefaultHttpResponseFactory;
import ch.boye.httpclientandroidlib.io.SessionInputBuffer;
import ch.boye.httpclientandroidlib.message.LineParser;
import ch.boye.httpclientandroidlib.message.ParserCursor;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* HTTP response parser that obtain its input from an instance
* of {@link SessionInputBuffer}.
*
* @since 4.2
*/
@SuppressWarnings("deprecation")
@NotThreadSafe
public class DefaultHttpResponseParser extends AbstractMessageParser<HttpResponse> {
private final HttpResponseFactory responseFactory;
private final CharArrayBuffer lineBuf;
/**
* Creates an instance of this class.
*
* @param buffer the session input buffer.
* @param lineParser the line parser.
* @param responseFactory the factory to use to create
* {@link HttpResponse}s.
* @param params HTTP parameters.
*
* @deprecated (4.3) use
* {@link DefaultHttpResponseParser#DefaultHttpResponseParser(SessionInputBuffer, LineParser,
* HttpResponseFactory, MessageConstraints)}
*/
@Deprecated
public DefaultHttpResponseParser(
final SessionInputBuffer buffer,
final LineParser lineParser,
final HttpResponseFactory responseFactory,
final HttpParams params) {
super(buffer, lineParser, params);
this.responseFactory = Args.notNull(responseFactory, "Response factory");
this.lineBuf = new CharArrayBuffer(128);
}
/**
* Creates new instance of DefaultHttpResponseParser.
*
* @param buffer the session input buffer.
* @param lineParser the line parser. If <code>null</code>
* {@link ch.boye.httpclientandroidlib.message.BasicLineParser#INSTANCE} will be used
* @param responseFactory the response factory. If <code>null</code>
* {@link DefaultHttpResponseFactory#INSTANCE} will be used.
* @param constraints the message constraints. If <code>null</code>
* {@link MessageConstraints#DEFAULT} will be used.
*
* @since 4.3
*/
public DefaultHttpResponseParser(
final SessionInputBuffer buffer,
final LineParser lineParser,
final HttpResponseFactory responseFactory,
final MessageConstraints constraints) {
super(buffer, lineParser, constraints);
this.responseFactory = responseFactory != null ? responseFactory :
DefaultHttpResponseFactory.INSTANCE;
this.lineBuf = new CharArrayBuffer(128);
}
/**
* @since 4.3
*/
public DefaultHttpResponseParser(
final SessionInputBuffer buffer,
final MessageConstraints constraints) {
this(buffer, null, null, constraints);
}
/**
* @since 4.3
*/
public DefaultHttpResponseParser(final SessionInputBuffer buffer) {
this(buffer, null, null, MessageConstraints.DEFAULT);
}
@Override
protected HttpResponse parseHead(
final SessionInputBuffer sessionBuffer)
throws IOException, HttpException, ParseException {
this.lineBuf.clear();
final int i = sessionBuffer.readLine(this.lineBuf);
if (i == -1) {
throw new NoHttpResponseException("The target server failed to respond");
}
//create the status line from the status string
final ParserCursor cursor = new ParserCursor(0, this.lineBuf.length());
final StatusLine statusline = lineParser.parseStatusLine(this.lineBuf, cursor);
return this.responseFactory.newHttpResponse(statusline, null);
}
}

View file

@ -0,0 +1,71 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpResponseFactory;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.config.MessageConstraints;
import ch.boye.httpclientandroidlib.impl.DefaultHttpResponseFactory;
import ch.boye.httpclientandroidlib.io.HttpMessageParser;
import ch.boye.httpclientandroidlib.io.HttpMessageParserFactory;
import ch.boye.httpclientandroidlib.io.SessionInputBuffer;
import ch.boye.httpclientandroidlib.message.BasicLineParser;
import ch.boye.httpclientandroidlib.message.LineParser;
/**
* Default factory for response message parsers.
*
* @since 4.3
*/
@Immutable
public class DefaultHttpResponseParserFactory implements HttpMessageParserFactory<HttpResponse> {
public static final DefaultHttpResponseParserFactory INSTANCE = new DefaultHttpResponseParserFactory();
private final LineParser lineParser;
private final HttpResponseFactory responseFactory;
public DefaultHttpResponseParserFactory(final LineParser lineParser,
final HttpResponseFactory responseFactory) {
super();
this.lineParser = lineParser != null ? lineParser : BasicLineParser.INSTANCE;
this.responseFactory = responseFactory != null ? responseFactory
: DefaultHttpResponseFactory.INSTANCE;
}
public DefaultHttpResponseParserFactory() {
this(null, null);
}
public HttpMessageParser<HttpResponse> create(final SessionInputBuffer buffer,
final MessageConstraints constraints) {
return new DefaultHttpResponseParser(buffer, lineParser, responseFactory, constraints);
}
}

View file

@ -0,0 +1,69 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.io.SessionOutputBuffer;
import ch.boye.httpclientandroidlib.message.LineFormatter;
/**
* HTTP response writer that serializes its output to an instance of {@link SessionOutputBuffer}.
*
* @since 4.3
*/
@NotThreadSafe
public class DefaultHttpResponseWriter extends AbstractMessageWriter<HttpResponse> {
/**
* Creates an instance of DefaultHttpResponseWriter.
*
* @param buffer the session output buffer.
* @param formatter the line formatter If <code>null</code>
* {@link ch.boye.httpclientandroidlib.message.BasicLineFormatter#INSTANCE}
* will be used.
*/
public DefaultHttpResponseWriter(
final SessionOutputBuffer buffer,
final LineFormatter formatter) {
super(buffer, formatter);
}
public DefaultHttpResponseWriter(final SessionOutputBuffer buffer) {
super(buffer, null);
}
@Override
protected void writeHeadLine(final HttpResponse message) throws IOException {
lineFormatter.formatStatusLine(this.lineBuf, message.getStatusLine());
this.sessionBuffer.writeLine(this.lineBuf);
}
}

View file

@ -0,0 +1,63 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.io.HttpMessageWriter;
import ch.boye.httpclientandroidlib.io.HttpMessageWriterFactory;
import ch.boye.httpclientandroidlib.io.SessionOutputBuffer;
import ch.boye.httpclientandroidlib.message.BasicLineFormatter;
import ch.boye.httpclientandroidlib.message.LineFormatter;
/**
* Default factory for response message writers.
*
* @since 4.3
*/
@Immutable
public class DefaultHttpResponseWriterFactory implements HttpMessageWriterFactory<HttpResponse> {
public static final DefaultHttpResponseWriterFactory INSTANCE = new DefaultHttpResponseWriterFactory();
private final LineFormatter lineFormatter;
public DefaultHttpResponseWriterFactory(final LineFormatter lineFormatter) {
super();
this.lineFormatter = lineFormatter != null ? lineFormatter : BasicLineFormatter.INSTANCE;
}
public DefaultHttpResponseWriterFactory() {
this(null);
}
public HttpMessageWriter<HttpResponse> create(final SessionOutputBuffer buffer) {
return new DefaultHttpResponseWriter(buffer, lineFormatter);
}
}

View file

@ -0,0 +1,102 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import ch.boye.httpclientandroidlib.ConnectionClosedException;
import ch.boye.httpclientandroidlib.HttpException;
import ch.boye.httpclientandroidlib.HttpMessage;
import ch.boye.httpclientandroidlib.HttpRequestFactory;
import ch.boye.httpclientandroidlib.ParseException;
import ch.boye.httpclientandroidlib.RequestLine;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.io.SessionInputBuffer;
import ch.boye.httpclientandroidlib.message.LineParser;
import ch.boye.httpclientandroidlib.message.ParserCursor;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* HTTP request parser that obtain its input from an instance
* of {@link SessionInputBuffer}.
* <p>
* The following parameters can be used to customize the behavior of this
* class:
* <ul>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_HEADER_COUNT}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_LINE_LENGTH}</li>
* </ul>
*
* @since 4.0
*
* @deprecated (4.2) use {@link DefaultHttpRequestParser}
*/
@Deprecated
@NotThreadSafe
public class HttpRequestParser extends AbstractMessageParser<HttpMessage> {
private final HttpRequestFactory requestFactory;
private final CharArrayBuffer lineBuf;
/**
* Creates an instance of this class.
*
* @param buffer the session input buffer.
* @param parser the line parser.
* @param requestFactory the factory to use to create
* {@link ch.boye.httpclientandroidlib.HttpRequest}s.
* @param params HTTP parameters.
*/
public HttpRequestParser(
final SessionInputBuffer buffer,
final LineParser parser,
final HttpRequestFactory requestFactory,
final HttpParams params) {
super(buffer, parser, params);
this.requestFactory = Args.notNull(requestFactory, "Request factory");
this.lineBuf = new CharArrayBuffer(128);
}
@Override
protected HttpMessage parseHead(
final SessionInputBuffer sessionBuffer)
throws IOException, HttpException, ParseException {
this.lineBuf.clear();
final int i = sessionBuffer.readLine(this.lineBuf);
if (i == -1) {
throw new ConnectionClosedException("Client closed connection");
}
final ParserCursor cursor = new ParserCursor(0, this.lineBuf.length());
final RequestLine requestline = this.lineParser.parseRequestLine(this.lineBuf, cursor);
return this.requestFactory.newHttpRequest(requestline);
}
}

View file

@ -0,0 +1,62 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.io.SessionOutputBuffer;
import ch.boye.httpclientandroidlib.message.LineFormatter;
import ch.boye.httpclientandroidlib.params.HttpParams;
/**
* HTTP request writer that serializes its output to an instance
* of {@link SessionOutputBuffer}.
*
* @since 4.0
*
* @deprecated (4.3) use {@link DefaultHttpRequestWriter}
*/
@NotThreadSafe
@Deprecated
public class HttpRequestWriter extends AbstractMessageWriter<HttpRequest> {
public HttpRequestWriter(final SessionOutputBuffer buffer,
final LineFormatter formatter,
final HttpParams params) {
super(buffer, formatter, params);
}
@Override
protected void writeHeadLine(final HttpRequest message) throws IOException {
lineFormatter.formatRequestLine(this.lineBuf, message.getRequestLine());
this.sessionBuffer.writeLine(this.lineBuf);
}
}

View file

@ -0,0 +1,103 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import ch.boye.httpclientandroidlib.HttpException;
import ch.boye.httpclientandroidlib.HttpMessage;
import ch.boye.httpclientandroidlib.HttpResponseFactory;
import ch.boye.httpclientandroidlib.NoHttpResponseException;
import ch.boye.httpclientandroidlib.ParseException;
import ch.boye.httpclientandroidlib.StatusLine;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.io.SessionInputBuffer;
import ch.boye.httpclientandroidlib.message.LineParser;
import ch.boye.httpclientandroidlib.message.ParserCursor;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* HTTP response parser that obtain its input from an instance
* of {@link SessionInputBuffer}.
* <p>
* The following parameters can be used to customize the behavior of this
* class:
* <ul>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_HEADER_COUNT}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_LINE_LENGTH}</li>
* </ul>
*
* @since 4.0
*
* @deprecated (4.2) use {@link DefaultHttpResponseParser}
*/
@Deprecated
@NotThreadSafe
public class HttpResponseParser extends AbstractMessageParser<HttpMessage> {
private final HttpResponseFactory responseFactory;
private final CharArrayBuffer lineBuf;
/**
* Creates an instance of this class.
*
* @param buffer the session input buffer.
* @param parser the line parser.
* @param responseFactory the factory to use to create
* {@link ch.boye.httpclientandroidlib.HttpResponse}s.
* @param params HTTP parameters.
*/
public HttpResponseParser(
final SessionInputBuffer buffer,
final LineParser parser,
final HttpResponseFactory responseFactory,
final HttpParams params) {
super(buffer, parser, params);
this.responseFactory = Args.notNull(responseFactory, "Response factory");
this.lineBuf = new CharArrayBuffer(128);
}
@Override
protected HttpMessage parseHead(
final SessionInputBuffer sessionBuffer)
throws IOException, HttpException, ParseException {
this.lineBuf.clear();
final int i = sessionBuffer.readLine(this.lineBuf);
if (i == -1) {
throw new NoHttpResponseException("The target server failed to respond");
}
//create the status line from the status string
final ParserCursor cursor = new ParserCursor(0, this.lineBuf.length());
final StatusLine statusline = lineParser.parseStatusLine(this.lineBuf, cursor);
return this.responseFactory.newHttpResponse(statusline, null);
}
}

View file

@ -0,0 +1,62 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.io.SessionOutputBuffer;
import ch.boye.httpclientandroidlib.message.LineFormatter;
import ch.boye.httpclientandroidlib.params.HttpParams;
/**
* HTTP response writer that serializes its output to an instance
* of {@link SessionOutputBuffer}.
*
* @since 4.0
*
* @deprecated (4.3) use {@link DefaultHttpResponseWriter}
*/
@NotThreadSafe
@Deprecated
public class HttpResponseWriter extends AbstractMessageWriter<HttpResponse> {
public HttpResponseWriter(final SessionOutputBuffer buffer,
final LineFormatter formatter,
final HttpParams params) {
super(buffer, formatter, params);
}
@Override
protected void writeHeadLine(final HttpResponse message) throws IOException {
lineFormatter.formatStatusLine(this.lineBuf, message.getStatusLine());
this.sessionBuffer.writeLine(this.lineBuf);
}
}

View file

@ -0,0 +1,63 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.io.HttpTransportMetrics;
/**
* Default implementation of {@link HttpTransportMetrics}.
*
* @since 4.0
*/
@NotThreadSafe
public class HttpTransportMetricsImpl implements HttpTransportMetrics {
private long bytesTransferred = 0;
public HttpTransportMetricsImpl() {
super();
}
public long getBytesTransferred() {
return this.bytesTransferred;
}
public void setBytesTransferred(final long count) {
this.bytesTransferred = count;
}
public void incrementBytesTransferred(final long count) {
this.bytesTransferred += count;
}
public void reset() {
this.bytesTransferred = 0;
}
}

View file

@ -0,0 +1,99 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import java.io.InputStream;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.io.BufferInfo;
import ch.boye.httpclientandroidlib.io.SessionInputBuffer;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Input stream that reads data without any transformation. The end of the
* content entity is demarcated by closing the underlying connection
* (EOF condition). Entities transferred using this input stream can be of
* unlimited length.
* <p>
* Note that this class NEVER closes the underlying stream, even when close
* gets called. Instead, it will read until the end of the stream (until
* <code>-1</code> is returned).
*
* @since 4.0
*/
@NotThreadSafe
public class IdentityInputStream extends InputStream {
private final SessionInputBuffer in;
private boolean closed = false;
/**
* Wraps session input stream and reads input until the the end of stream.
*
* @param in The session input buffer
*/
public IdentityInputStream(final SessionInputBuffer in) {
super();
this.in = Args.notNull(in, "Session input buffer");
}
@Override
public int available() throws IOException {
if (this.in instanceof BufferInfo) {
return ((BufferInfo) this.in).length();
} else {
return 0;
}
}
@Override
public void close() throws IOException {
this.closed = true;
}
@Override
public int read() throws IOException {
if (this.closed) {
return -1;
} else {
return this.in.read();
}
}
@Override
public int read(final byte[] b, final int off, final int len) throws IOException {
if (this.closed) {
return -1;
} else {
return this.in.read(b, off, len);
}
}
}

View file

@ -0,0 +1,104 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import java.io.OutputStream;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.io.SessionOutputBuffer;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Output stream that writes data without any transformation. The end of
* the content entity is demarcated by closing the underlying connection
* (EOF condition). Entities transferred using this input stream can be of
* unlimited length.
* <p>
* Note that this class NEVER closes the underlying stream, even when close
* gets called. Instead, the stream will be marked as closed and no further
* output will be permitted.
*
* @since 4.0
*/
@NotThreadSafe
public class IdentityOutputStream extends OutputStream {
/**
* Wrapped session output buffer.
*/
private final SessionOutputBuffer out;
/** True if the stream is closed. */
private boolean closed = false;
public IdentityOutputStream(final SessionOutputBuffer out) {
super();
this.out = Args.notNull(out, "Session output buffer");
}
/**
* <p>Does not close the underlying socket output.</p>
*
* @throws IOException If an I/O problem occurs.
*/
@Override
public void close() throws IOException {
if (!this.closed) {
this.closed = true;
this.out.flush();
}
}
@Override
public void flush() throws IOException {
this.out.flush();
}
@Override
public void write(final byte[] b, final int off, final int len) throws IOException {
if (this.closed) {
throw new IOException("Attempted write to closed stream.");
}
this.out.write(b, off, len);
}
@Override
public void write(final byte[] b) throws IOException {
write(b, 0, b.length);
}
@Override
public void write(final int b) throws IOException {
if (this.closed) {
throw new IOException("Attempted write to closed stream.");
}
this.out.write(b);
}
}

View file

@ -0,0 +1,399 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import ch.boye.httpclientandroidlib.MessageConstraintException;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.config.MessageConstraints;
import ch.boye.httpclientandroidlib.io.BufferInfo;
import ch.boye.httpclientandroidlib.io.HttpTransportMetrics;
import ch.boye.httpclientandroidlib.io.SessionInputBuffer;
import ch.boye.httpclientandroidlib.protocol.HTTP;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.Asserts;
import ch.boye.httpclientandroidlib.util.ByteArrayBuffer;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* Abstract base class for session input buffers that stream data from
* an arbitrary {@link InputStream}. This class buffers input data in
* an internal byte array for optimal input performance.
* <p/>
* {@link #readLine(CharArrayBuffer)} and {@link #readLine()} methods of this
* class treat a lone LF as valid line delimiters in addition to CR-LF required
* by the HTTP specification.
*
* @since 4.3
*/
@NotThreadSafe
public class SessionInputBufferImpl implements SessionInputBuffer, BufferInfo {
private final HttpTransportMetricsImpl metrics;
private final byte[] buffer;
private final ByteArrayBuffer linebuffer;
private final int minChunkLimit;
private final MessageConstraints constraints;
private final CharsetDecoder decoder;
private InputStream instream;
private int bufferpos;
private int bufferlen;
private CharBuffer cbuf;
/**
* Creates new instance of SessionInputBufferImpl.
*
* @param metrics HTTP transport metrics.
* @param buffersize buffer size. Must be a positive number.
* @param minChunkLimit size limit below which data chunks should be buffered in memory
* in order to minimize native method invocations on the underlying network socket.
* The optimal value of this parameter can be platform specific and defines a trade-off
* between performance of memory copy operations and that of native method invocation.
* If negative default chunk limited will be used.
* @param constraints Message constraints. If <code>null</code>
* {@link MessageConstraints#DEFAULT} will be used.
* @param chardecoder chardecoder to be used for decoding HTTP protocol elements.
* If <code>null</code> simple type cast will be used for byte to char conversion.
*/
public SessionInputBufferImpl(
final HttpTransportMetricsImpl metrics,
final int buffersize,
final int minChunkLimit,
final MessageConstraints constraints,
final CharsetDecoder chardecoder) {
Args.notNull(metrics, "HTTP transport metrcis");
Args.positive(buffersize, "Buffer size");
this.metrics = metrics;
this.buffer = new byte[buffersize];
this.bufferpos = 0;
this.bufferlen = 0;
this.minChunkLimit = minChunkLimit >= 0 ? minChunkLimit : 512;
this.constraints = constraints != null ? constraints : MessageConstraints.DEFAULT;
this.linebuffer = new ByteArrayBuffer(buffersize);
this.decoder = chardecoder;
}
public SessionInputBufferImpl(
final HttpTransportMetricsImpl metrics,
final int buffersize) {
this(metrics, buffersize, buffersize, null, null);
}
public void bind(final InputStream instream) {
this.instream = instream;
}
public boolean isBound() {
return this.instream != null;
}
public int capacity() {
return this.buffer.length;
}
public int length() {
return this.bufferlen - this.bufferpos;
}
public int available() {
return capacity() - length();
}
private int streamRead(final byte[] b, final int off, final int len) throws IOException {
Asserts.notNull(this.instream, "Input stream");
return this.instream.read(b, off, len);
}
public int fillBuffer() throws IOException {
// compact the buffer if necessary
if (this.bufferpos > 0) {
final int len = this.bufferlen - this.bufferpos;
if (len > 0) {
System.arraycopy(this.buffer, this.bufferpos, this.buffer, 0, len);
}
this.bufferpos = 0;
this.bufferlen = len;
}
final int l;
final int off = this.bufferlen;
final int len = this.buffer.length - off;
l = streamRead(this.buffer, off, len);
if (l == -1) {
return -1;
} else {
this.bufferlen = off + l;
this.metrics.incrementBytesTransferred(l);
return l;
}
}
public boolean hasBufferedData() {
return this.bufferpos < this.bufferlen;
}
public void clear() {
this.bufferpos = 0;
this.bufferlen = 0;
}
public int read() throws IOException {
int noRead;
while (!hasBufferedData()) {
noRead = fillBuffer();
if (noRead == -1) {
return -1;
}
}
return this.buffer[this.bufferpos++] & 0xff;
}
public int read(final byte[] b, final int off, final int len) throws IOException {
if (b == null) {
return 0;
}
if (hasBufferedData()) {
final int chunk = Math.min(len, this.bufferlen - this.bufferpos);
System.arraycopy(this.buffer, this.bufferpos, b, off, chunk);
this.bufferpos += chunk;
return chunk;
}
// If the remaining capacity is big enough, read directly from the
// underlying input stream bypassing the buffer.
if (len > this.minChunkLimit) {
final int read = streamRead(b, off, len);
if (read > 0) {
this.metrics.incrementBytesTransferred(read);
}
return read;
} else {
// otherwise read to the buffer first
while (!hasBufferedData()) {
final int noRead = fillBuffer();
if (noRead == -1) {
return -1;
}
}
final int chunk = Math.min(len, this.bufferlen - this.bufferpos);
System.arraycopy(this.buffer, this.bufferpos, b, off, chunk);
this.bufferpos += chunk;
return chunk;
}
}
public int read(final byte[] b) throws IOException {
if (b == null) {
return 0;
}
return read(b, 0, b.length);
}
private int locateLF() {
for (int i = this.bufferpos; i < this.bufferlen; i++) {
if (this.buffer[i] == HTTP.LF) {
return i;
}
}
return -1;
}
/**
* Reads a complete line of characters up to a line delimiter from this
* session buffer into the given line buffer. The number of chars actually
* read is returned as an integer. The line delimiter itself is discarded.
* If no char is available because the end of the stream has been reached,
* the value <code>-1</code> is returned. This method blocks until input
* data is available, end of file is detected, or an exception is thrown.
* <p>
* This method treats a lone LF as a valid line delimiters in addition
* to CR-LF required by the HTTP specification.
*
* @param charbuffer the line buffer.
* @return one line of characters
* @exception IOException if an I/O error occurs.
*/
public int readLine(final CharArrayBuffer charbuffer) throws IOException {
Args.notNull(charbuffer, "Char array buffer");
int noRead = 0;
boolean retry = true;
while (retry) {
// attempt to find end of line (LF)
final int i = locateLF();
if (i != -1) {
// end of line found.
if (this.linebuffer.isEmpty()) {
// the entire line is preset in the read buffer
return lineFromReadBuffer(charbuffer, i);
}
retry = false;
final int len = i + 1 - this.bufferpos;
this.linebuffer.append(this.buffer, this.bufferpos, len);
this.bufferpos = i + 1;
} else {
// end of line not found
if (hasBufferedData()) {
final int len = this.bufferlen - this.bufferpos;
this.linebuffer.append(this.buffer, this.bufferpos, len);
this.bufferpos = this.bufferlen;
}
noRead = fillBuffer();
if (noRead == -1) {
retry = false;
}
}
final int maxLineLen = this.constraints.getMaxLineLength();
if (maxLineLen > 0 && this.linebuffer.length() >= maxLineLen) {
throw new MessageConstraintException("Maximum line length limit exceeded");
}
}
if (noRead == -1 && this.linebuffer.isEmpty()) {
// indicate the end of stream
return -1;
}
return lineFromLineBuffer(charbuffer);
}
/**
* Reads a complete line of characters up to a line delimiter from this
* session buffer. The line delimiter itself is discarded. If no char is
* available because the end of the stream has been reached,
* <code>null</code> is returned. This method blocks until input data is
* available, end of file is detected, or an exception is thrown.
* <p>
* This method treats a lone LF as a valid line delimiters in addition
* to CR-LF required by the HTTP specification.
*
* @return HTTP line as a string
* @exception IOException if an I/O error occurs.
*/
private int lineFromLineBuffer(final CharArrayBuffer charbuffer)
throws IOException {
// discard LF if found
int len = this.linebuffer.length();
if (len > 0) {
if (this.linebuffer.byteAt(len - 1) == HTTP.LF) {
len--;
}
// discard CR if found
if (len > 0) {
if (this.linebuffer.byteAt(len - 1) == HTTP.CR) {
len--;
}
}
}
if (this.decoder == null) {
charbuffer.append(this.linebuffer, 0, len);
} else {
final ByteBuffer bbuf = ByteBuffer.wrap(this.linebuffer.buffer(), 0, len);
len = appendDecoded(charbuffer, bbuf);
}
this.linebuffer.clear();
return len;
}
private int lineFromReadBuffer(final CharArrayBuffer charbuffer, final int position)
throws IOException {
int pos = position;
final int off = this.bufferpos;
int len;
this.bufferpos = pos + 1;
if (pos > off && this.buffer[pos - 1] == HTTP.CR) {
// skip CR if found
pos--;
}
len = pos - off;
if (this.decoder == null) {
charbuffer.append(this.buffer, off, len);
} else {
final ByteBuffer bbuf = ByteBuffer.wrap(this.buffer, off, len);
len = appendDecoded(charbuffer, bbuf);
}
return len;
}
private int appendDecoded(
final CharArrayBuffer charbuffer, final ByteBuffer bbuf) throws IOException {
if (!bbuf.hasRemaining()) {
return 0;
}
if (this.cbuf == null) {
this.cbuf = CharBuffer.allocate(1024);
}
this.decoder.reset();
int len = 0;
while (bbuf.hasRemaining()) {
final CoderResult result = this.decoder.decode(bbuf, this.cbuf, true);
len += handleDecodingResult(result, charbuffer, bbuf);
}
final CoderResult result = this.decoder.flush(this.cbuf);
len += handleDecodingResult(result, charbuffer, bbuf);
this.cbuf.clear();
return len;
}
private int handleDecodingResult(
final CoderResult result,
final CharArrayBuffer charbuffer,
final ByteBuffer bbuf) throws IOException {
if (result.isError()) {
result.throwException();
}
this.cbuf.flip();
final int len = this.cbuf.remaining();
while (this.cbuf.hasRemaining()) {
charbuffer.append(this.cbuf.get());
}
this.cbuf.compact();
return len;
}
public String readLine() throws IOException {
final CharArrayBuffer charbuffer = new CharArrayBuffer(64);
final int l = readLine(charbuffer);
if (l != -1) {
return charbuffer.toString();
} else {
return null;
}
}
public boolean isDataAvailable(final int timeout) throws IOException {
return hasBufferedData();
}
public HttpTransportMetrics getMetrics() {
return this.metrics;
}
}

View file

@ -0,0 +1,283 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.io.BufferInfo;
import ch.boye.httpclientandroidlib.io.HttpTransportMetrics;
import ch.boye.httpclientandroidlib.io.SessionOutputBuffer;
import ch.boye.httpclientandroidlib.protocol.HTTP;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.Asserts;
import ch.boye.httpclientandroidlib.util.ByteArrayBuffer;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* Abstract base class for session output buffers that stream data to
* an arbitrary {@link OutputStream}. This class buffers small chunks of
* output data in an internal byte array for optimal output performance.
* </p>
* {@link #writeLine(CharArrayBuffer)} and {@link #writeLine(String)} methods
* of this class use CR-LF as a line delimiter.
*
* @since 4.3
*/
@NotThreadSafe
public class SessionOutputBufferImpl implements SessionOutputBuffer, BufferInfo {
private static final byte[] CRLF = new byte[] {HTTP.CR, HTTP.LF};
private final HttpTransportMetricsImpl metrics;
private final ByteArrayBuffer buffer;
private final int fragementSizeHint;
private final CharsetEncoder encoder;
private OutputStream outstream;
private ByteBuffer bbuf;
/**
* Creates new instance of SessionOutputBufferImpl.
*
* @param metrics HTTP transport metrics.
* @param buffersize buffer size. Must be a positive number.
* @param fragementSizeHint fragment size hint defining a minimal size of a fragment
* that should be written out directly to the socket bypassing the session buffer.
* Value <code>0</code> disables fragment buffering.
* @param charencoder charencoder to be used for encoding HTTP protocol elements.
* If <code>null</code> simple type cast will be used for char to byte conversion.
*/
public SessionOutputBufferImpl(
final HttpTransportMetricsImpl metrics,
final int buffersize,
final int fragementSizeHint,
final CharsetEncoder charencoder) {
super();
Args.positive(buffersize, "Buffer size");
Args.notNull(metrics, "HTTP transport metrcis");
this.metrics = metrics;
this.buffer = new ByteArrayBuffer(buffersize);
this.fragementSizeHint = fragementSizeHint >= 0 ? fragementSizeHint : 0;
this.encoder = charencoder;
}
public SessionOutputBufferImpl(
final HttpTransportMetricsImpl metrics,
final int buffersize) {
this(metrics, buffersize, buffersize, null);
}
public void bind(final OutputStream outstream) {
this.outstream = outstream;
}
public boolean isBound() {
return this.outstream != null;
}
public int capacity() {
return this.buffer.capacity();
}
public int length() {
return this.buffer.length();
}
public int available() {
return capacity() - length();
}
private void streamWrite(final byte[] b, final int off, final int len) throws IOException {
Asserts.notNull(outstream, "Output stream");
this.outstream.write(b, off, len);
}
private void flushStream() throws IOException {
if (this.outstream != null) {
this.outstream.flush();
}
}
private void flushBuffer() throws IOException {
final int len = this.buffer.length();
if (len > 0) {
streamWrite(this.buffer.buffer(), 0, len);
this.buffer.clear();
this.metrics.incrementBytesTransferred(len);
}
}
public void flush() throws IOException {
flushBuffer();
flushStream();
}
public void write(final byte[] b, final int off, final int len) throws IOException {
if (b == null) {
return;
}
// Do not want to buffer large-ish chunks
// if the byte array is larger then MIN_CHUNK_LIMIT
// write it directly to the output stream
if (len > this.fragementSizeHint || len > this.buffer.capacity()) {
// flush the buffer
flushBuffer();
// write directly to the out stream
streamWrite(b, off, len);
this.metrics.incrementBytesTransferred(len);
} else {
// Do not let the buffer grow unnecessarily
final int freecapacity = this.buffer.capacity() - this.buffer.length();
if (len > freecapacity) {
// flush the buffer
flushBuffer();
}
// buffer
this.buffer.append(b, off, len);
}
}
public void write(final byte[] b) throws IOException {
if (b == null) {
return;
}
write(b, 0, b.length);
}
public void write(final int b) throws IOException {
if (this.fragementSizeHint > 0) {
if (this.buffer.isFull()) {
flushBuffer();
}
this.buffer.append(b);
} else {
flushBuffer();
this.outstream.write(b);
}
}
/**
* Writes characters from the specified string followed by a line delimiter
* to this session buffer.
* <p>
* This method uses CR-LF as a line delimiter.
*
* @param s the line.
* @exception IOException if an I/O error occurs.
*/
public void writeLine(final String s) throws IOException {
if (s == null) {
return;
}
if (s.length() > 0) {
if (this.encoder == null) {
for (int i = 0; i < s.length(); i++) {
write(s.charAt(i));
}
} else {
final CharBuffer cbuf = CharBuffer.wrap(s);
writeEncoded(cbuf);
}
}
write(CRLF);
}
/**
* Writes characters from the specified char array followed by a line
* delimiter to this session buffer.
* <p>
* This method uses CR-LF as a line delimiter.
*
* @param charbuffer the buffer containing chars of the line.
* @exception IOException if an I/O error occurs.
*/
public void writeLine(final CharArrayBuffer charbuffer) throws IOException {
if (charbuffer == null) {
return;
}
if (this.encoder == null) {
int off = 0;
int remaining = charbuffer.length();
while (remaining > 0) {
int chunk = this.buffer.capacity() - this.buffer.length();
chunk = Math.min(chunk, remaining);
if (chunk > 0) {
this.buffer.append(charbuffer, off, chunk);
}
if (this.buffer.isFull()) {
flushBuffer();
}
off += chunk;
remaining -= chunk;
}
} else {
final CharBuffer cbuf = CharBuffer.wrap(charbuffer.buffer(), 0, charbuffer.length());
writeEncoded(cbuf);
}
write(CRLF);
}
private void writeEncoded(final CharBuffer cbuf) throws IOException {
if (!cbuf.hasRemaining()) {
return;
}
if (this.bbuf == null) {
this.bbuf = ByteBuffer.allocate(1024);
}
this.encoder.reset();
while (cbuf.hasRemaining()) {
final CoderResult result = this.encoder.encode(cbuf, this.bbuf, true);
handleEncodingResult(result);
}
final CoderResult result = this.encoder.flush(this.bbuf);
handleEncodingResult(result);
this.bbuf.clear();
}
private void handleEncodingResult(final CoderResult result) throws IOException {
if (result.isError()) {
result.throwException();
}
this.bbuf.flip();
while (this.bbuf.hasRemaining()) {
write(this.bbuf.get());
}
this.bbuf.compact();
}
public HttpTransportMetrics getMetrics() {
return this.metrics;
}
}

View file

@ -0,0 +1,108 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import java.net.Socket;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.io.EofSensor;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.util.Args;
/**
* {@link ch.boye.httpclientandroidlib.io.SessionInputBuffer} implementation
* bound to a {@link Socket}.
*
* @since 4.0
*
* @deprecated (4.3) use {@link SessionInputBufferImpl}
*/
@NotThreadSafe
@Deprecated
public class SocketInputBuffer extends AbstractSessionInputBuffer implements EofSensor {
private final Socket socket;
private boolean eof;
/**
* Creates an instance of this class.
*
* @param socket the socket to read data from.
* @param buffersize the size of the internal buffer. If this number is less
* than <code>0</code> it is set to the value of
* {@link Socket#getReceiveBufferSize()}. If resultant number is less
* than <code>1024</code> it is set to <code>1024</code>.
* @param params HTTP parameters.
*/
public SocketInputBuffer(
final Socket socket,
final int buffersize,
final HttpParams params) throws IOException {
super();
Args.notNull(socket, "Socket");
this.socket = socket;
this.eof = false;
int n = buffersize;
if (n < 0) {
n = socket.getReceiveBufferSize();
}
if (n < 1024) {
n = 1024;
}
init(socket.getInputStream(), n, params);
}
@Override
protected int fillBuffer() throws IOException {
final int i = super.fillBuffer();
this.eof = i == -1;
return i;
}
public boolean isDataAvailable(final int timeout) throws IOException {
boolean result = hasBufferedData();
if (!result) {
final int oldtimeout = this.socket.getSoTimeout();
try {
this.socket.setSoTimeout(timeout);
fillBuffer();
result = hasBufferedData();
} finally {
socket.setSoTimeout(oldtimeout);
}
}
return result;
}
public boolean isEof() {
return this.eof;
}
}

View file

@ -0,0 +1,75 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.io;
import java.io.IOException;
import java.net.Socket;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.util.Args;
/**
* {@link ch.boye.httpclientandroidlib.io.SessionOutputBuffer} implementation
* bound to a {@link Socket}.
*
* @since 4.0
*
* @deprecated (4.3) use {@link SessionOutputBufferImpl}
*/
@NotThreadSafe
@Deprecated
public class SocketOutputBuffer extends AbstractSessionOutputBuffer {
/**
* Creates an instance of this class.
*
* @param socket the socket to write data to.
* @param buffersize the size of the internal buffer. If this number is less
* than <code>0</code> it is set to the value of
* {@link Socket#getSendBufferSize()}. If resultant number is less
* than <code>1024</code> it is set to <code>1024</code>.
* @param params HTTP parameters.
*/
public SocketOutputBuffer(
final Socket socket,
final int buffersize,
final HttpParams params) throws IOException {
super();
Args.notNull(socket, "Socket");
int n = buffersize;
if (n < 0) {
n = socket.getSendBufferSize();
}
if (n < 1024) {
n = 1024;
}
init(socket.getOutputStream(), n, params);
}
}

View file

@ -0,0 +1,32 @@
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
/**
* Default implementations of message parses and writers
* for synchronous, blocking communication.
*/
package ch.boye.httpclientandroidlib.impl.io;