import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,164 @@
/*
* ====================================================================
* 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.message;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HeaderIterator;
import ch.boye.httpclientandroidlib.HttpMessage;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.params.BasicHttpParams;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Basic implementation of {@link HttpMessage}.
*
* @since 4.0
*/
@SuppressWarnings("deprecation")
@NotThreadSafe
public abstract class AbstractHttpMessage implements HttpMessage {
protected HeaderGroup headergroup;
@Deprecated
protected HttpParams params;
/**
* @deprecated (4.3) use {@link AbstractHttpMessage#AbstractHttpMessage()}
*/
@Deprecated
protected AbstractHttpMessage(final HttpParams params) {
super();
this.headergroup = new HeaderGroup();
this.params = params;
}
protected AbstractHttpMessage() {
this(null);
}
// non-javadoc, see interface HttpMessage
public boolean containsHeader(final String name) {
return this.headergroup.containsHeader(name);
}
// non-javadoc, see interface HttpMessage
public Header[] getHeaders(final String name) {
return this.headergroup.getHeaders(name);
}
// non-javadoc, see interface HttpMessage
public Header getFirstHeader(final String name) {
return this.headergroup.getFirstHeader(name);
}
// non-javadoc, see interface HttpMessage
public Header getLastHeader(final String name) {
return this.headergroup.getLastHeader(name);
}
// non-javadoc, see interface HttpMessage
public Header[] getAllHeaders() {
return this.headergroup.getAllHeaders();
}
// non-javadoc, see interface HttpMessage
public void addHeader(final Header header) {
this.headergroup.addHeader(header);
}
// non-javadoc, see interface HttpMessage
public void addHeader(final String name, final String value) {
Args.notNull(name, "Header name");
this.headergroup.addHeader(new BasicHeader(name, value));
}
// non-javadoc, see interface HttpMessage
public void setHeader(final Header header) {
this.headergroup.updateHeader(header);
}
// non-javadoc, see interface HttpMessage
public void setHeader(final String name, final String value) {
Args.notNull(name, "Header name");
this.headergroup.updateHeader(new BasicHeader(name, value));
}
// non-javadoc, see interface HttpMessage
public void setHeaders(final Header[] headers) {
this.headergroup.setHeaders(headers);
}
// non-javadoc, see interface HttpMessage
public void removeHeader(final Header header) {
this.headergroup.removeHeader(header);
}
// non-javadoc, see interface HttpMessage
public void removeHeaders(final String name) {
if (name == null) {
return;
}
for (final HeaderIterator i = this.headergroup.iterator(); i.hasNext(); ) {
final Header header = i.nextHeader();
if (name.equalsIgnoreCase(header.getName())) {
i.remove();
}
}
}
// non-javadoc, see interface HttpMessage
public HeaderIterator headerIterator() {
return this.headergroup.iterator();
}
// non-javadoc, see interface HttpMessage
public HeaderIterator headerIterator(final String name) {
return this.headergroup.iterator(name);
}
/**
* @deprecated (4.3) use constructor parameters of configuration API provided by HttpClient
*/
@Deprecated
public HttpParams getParams() {
if (this.params == null) {
this.params = new BasicHttpParams();
}
return this.params;
}
/**
* @deprecated (4.3) use constructor parameters of configuration API provided by HttpClient
*/
@Deprecated
public void setParams(final HttpParams params) {
this.params = Args.notNull(params, "HTTP parameters");
}
}

View file

@ -0,0 +1,91 @@
/*
* ====================================================================
* 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.message;
import java.io.Serializable;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.ParseException;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Basic implementation of {@link Header}.
*
* @since 4.0
*/
@Immutable
public class BasicHeader implements Header, Cloneable, Serializable {
private static final long serialVersionUID = -5427236326487562174L;
private final String name;
private final String value;
/**
* Constructor with name and value
*
* @param name the header name
* @param value the header value
*/
public BasicHeader(final String name, final String value) {
super();
this.name = Args.notNull(name, "Name");
this.value = value;
}
public String getName() {
return this.name;
}
public String getValue() {
return this.value;
}
@Override
public String toString() {
// no need for non-default formatting in toString()
return BasicLineFormatter.INSTANCE.formatHeader(null, this).toString();
}
public HeaderElement[] getElements() throws ParseException {
if (this.value != null) {
// result intentionally not cached, it's probably not used again
return BasicHeaderValueParser.parseElements(this.value, null);
} else {
return new HeaderElement[] {};
}
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

View file

@ -0,0 +1,162 @@
/*
* ====================================================================
* 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.message;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.NameValuePair;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.LangUtils;
/**
* Basic implementation of {@link HeaderElement}
*
* @since 4.0
*/
@NotThreadSafe
public class BasicHeaderElement implements HeaderElement, Cloneable {
private final String name;
private final String value;
private final NameValuePair[] parameters;
/**
* Constructor with name, value and parameters.
*
* @param name header element name
* @param value header element value. May be <tt>null</tt>
* @param parameters header element parameters. May be <tt>null</tt>.
* Parameters are copied by reference, not by value
*/
public BasicHeaderElement(
final String name,
final String value,
final NameValuePair[] parameters) {
super();
this.name = Args.notNull(name, "Name");
this.value = value;
if (parameters != null) {
this.parameters = parameters;
} else {
this.parameters = new NameValuePair[] {};
}
}
/**
* Constructor with name and value.
*
* @param name header element name
* @param value header element value. May be <tt>null</tt>
*/
public BasicHeaderElement(final String name, final String value) {
this(name, value, null);
}
public String getName() {
return this.name;
}
public String getValue() {
return this.value;
}
public NameValuePair[] getParameters() {
return this.parameters.clone();
}
public int getParameterCount() {
return this.parameters.length;
}
public NameValuePair getParameter(final int index) {
// ArrayIndexOutOfBoundsException is appropriate
return this.parameters[index];
}
public NameValuePair getParameterByName(final String name) {
Args.notNull(name, "Name");
NameValuePair found = null;
for (final NameValuePair current : this.parameters) {
if (current.getName().equalsIgnoreCase(name)) {
found = current;
break;
}
}
return found;
}
@Override
public boolean equals(final Object object) {
if (this == object) {
return true;
}
if (object instanceof HeaderElement) {
final BasicHeaderElement that = (BasicHeaderElement) object;
return this.name.equals(that.name)
&& LangUtils.equals(this.value, that.value)
&& LangUtils.equals(this.parameters, that.parameters);
} else {
return false;
}
}
@Override
public int hashCode() {
int hash = LangUtils.HASH_SEED;
hash = LangUtils.hashCode(hash, this.name);
hash = LangUtils.hashCode(hash, this.value);
for (final NameValuePair parameter : this.parameters) {
hash = LangUtils.hashCode(hash, parameter);
}
return hash;
}
@Override
public String toString() {
final StringBuilder buffer = new StringBuilder();
buffer.append(this.name);
if (this.value != null) {
buffer.append("=");
buffer.append(this.value);
}
for (final NameValuePair parameter : this.parameters) {
buffer.append("; ");
buffer.append(parameter);
}
return buffer.toString();
}
@Override
public Object clone() throws CloneNotSupportedException {
// parameters array is considered immutable
// no need to make a copy of it
return super.clone();
}
}

View file

@ -0,0 +1,151 @@
/*
* ====================================================================
* 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.message;
import java.util.NoSuchElementException;
import ch.boye.httpclientandroidlib.FormattedHeader;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.HeaderElementIterator;
import ch.boye.httpclientandroidlib.HeaderIterator;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* Basic implementation of a {@link HeaderElementIterator}.
*
* @since 4.0
*/
@NotThreadSafe
public class BasicHeaderElementIterator implements HeaderElementIterator {
private final HeaderIterator headerIt;
private final HeaderValueParser parser;
private HeaderElement currentElement = null;
private CharArrayBuffer buffer = null;
private ParserCursor cursor = null;
/**
* Creates a new instance of BasicHeaderElementIterator
*/
public BasicHeaderElementIterator(
final HeaderIterator headerIterator,
final HeaderValueParser parser) {
this.headerIt = Args.notNull(headerIterator, "Header iterator");
this.parser = Args.notNull(parser, "Parser");
}
public BasicHeaderElementIterator(final HeaderIterator headerIterator) {
this(headerIterator, BasicHeaderValueParser.INSTANCE);
}
private void bufferHeaderValue() {
this.cursor = null;
this.buffer = null;
while (this.headerIt.hasNext()) {
final Header h = this.headerIt.nextHeader();
if (h instanceof FormattedHeader) {
this.buffer = ((FormattedHeader) h).getBuffer();
this.cursor = new ParserCursor(0, this.buffer.length());
this.cursor.updatePos(((FormattedHeader) h).getValuePos());
break;
} else {
final String value = h.getValue();
if (value != null) {
this.buffer = new CharArrayBuffer(value.length());
this.buffer.append(value);
this.cursor = new ParserCursor(0, this.buffer.length());
break;
}
}
}
}
private void parseNextElement() {
// loop while there are headers left to parse
while (this.headerIt.hasNext() || this.cursor != null) {
if (this.cursor == null || this.cursor.atEnd()) {
// get next header value
bufferHeaderValue();
}
// Anything buffered?
if (this.cursor != null) {
// loop while there is data in the buffer
while (!this.cursor.atEnd()) {
final HeaderElement e = this.parser.parseHeaderElement(this.buffer, this.cursor);
if (!(e.getName().length() == 0 && e.getValue() == null)) {
// Found something
this.currentElement = e;
return;
}
}
// if at the end of the buffer
if (this.cursor.atEnd()) {
// discard it
this.cursor = null;
this.buffer = null;
}
}
}
}
public boolean hasNext() {
if (this.currentElement == null) {
parseNextElement();
}
return this.currentElement != null;
}
public HeaderElement nextElement() throws NoSuchElementException {
if (this.currentElement == null) {
parseNextElement();
}
if (this.currentElement == null) {
throw new NoSuchElementException("No more header elements available");
}
final HeaderElement element = this.currentElement;
this.currentElement = null;
return element;
}
public final Object next() throws NoSuchElementException {
return nextElement();
}
public void remove() throws UnsupportedOperationException {
throw new UnsupportedOperationException("Remove not supported");
}
}

View file

@ -0,0 +1,175 @@
/*
* ====================================================================
* 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.message;
import java.util.NoSuchElementException;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HeaderIterator;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Basic implementation of a {@link HeaderIterator}.
*
* @since 4.0
*/
@NotThreadSafe
public class BasicHeaderIterator implements HeaderIterator {
/**
* An array of headers to iterate over.
* Not all elements of this array are necessarily part of the iteration.
* This array will never be modified by the iterator.
* Derived implementations are expected to adhere to this restriction.
*/
protected final Header[] allHeaders;
/**
* The position of the next header in {@link #allHeaders allHeaders}.
* Negative if the iteration is over.
*/
protected int currentIndex;
/**
* The header name to filter by.
* <code>null</code> to iterate over all headers in the array.
*/
protected String headerName;
/**
* Creates a new header iterator.
*
* @param headers an array of headers over which to iterate
* @param name the name of the headers over which to iterate, or
* <code>null</code> for any
*/
public BasicHeaderIterator(final Header[] headers, final String name) {
super();
this.allHeaders = Args.notNull(headers, "Header array");
this.headerName = name;
this.currentIndex = findNext(-1);
}
/**
* Determines the index of the next header.
*
* @param pos one less than the index to consider first,
* -1 to search for the first header
*
* @return the index of the next header that matches the filter name,
* or negative if there are no more headers
*/
protected int findNext(final int pos) {
int from = pos;
if (from < -1) {
return -1;
}
final int to = this.allHeaders.length-1;
boolean found = false;
while (!found && (from < to)) {
from++;
found = filterHeader(from);
}
return found ? from : -1;
}
/**
* Checks whether a header is part of the iteration.
*
* @param index the index of the header to check
*
* @return <code>true</code> if the header should be part of the
* iteration, <code>false</code> to skip
*/
protected boolean filterHeader(final int index) {
return (this.headerName == null) ||
this.headerName.equalsIgnoreCase(this.allHeaders[index].getName());
}
// non-javadoc, see interface HeaderIterator
public boolean hasNext() {
return (this.currentIndex >= 0);
}
/**
* Obtains the next header from this iteration.
*
* @return the next header in this iteration
*
* @throws NoSuchElementException if there are no more headers
*/
public Header nextHeader()
throws NoSuchElementException {
final int current = this.currentIndex;
if (current < 0) {
throw new NoSuchElementException("Iteration already finished.");
}
this.currentIndex = findNext(current);
return this.allHeaders[current];
}
/**
* Returns the next header.
* Same as {@link #nextHeader nextHeader}, but not type-safe.
*
* @return the next header in this iteration
*
* @throws NoSuchElementException if there are no more headers
*/
public final Object next()
throws NoSuchElementException {
return nextHeader();
}
/**
* Removing headers is not supported.
*
* @throws UnsupportedOperationException always
*/
public void remove()
throws UnsupportedOperationException {
throw new UnsupportedOperationException
("Removing headers is not supported.");
}
}

View file

@ -0,0 +1,422 @@
/*
* ====================================================================
* 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.message;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.NameValuePair;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* Basic implementation for formatting header value elements.
* Instances of this class are stateless and thread-safe.
* Derived classes are expected to maintain these properties.
*
* @since 4.0
*/
@Immutable
public class BasicHeaderValueFormatter implements HeaderValueFormatter {
/**
* A default instance of this class, for use as default or fallback.
* Note that {@link BasicHeaderValueFormatter} is not a singleton, there
* can be many instances of the class itself and of derived classes.
* The instance here provides non-customized, default behavior.
*
* @deprecated (4.3) use {@link #INSTANCE}
*/
@Deprecated
public final static
BasicHeaderValueFormatter DEFAULT = new BasicHeaderValueFormatter();
public final static BasicHeaderValueFormatter INSTANCE = new BasicHeaderValueFormatter();
/**
* Special characters that can be used as separators in HTTP parameters.
* These special characters MUST be in a quoted string to be used within
* a parameter value .
*/
public final static String SEPARATORS = " ;,:@()<>\\\"/[]?={}\t";
/**
* Unsafe special characters that must be escaped using the backslash
* character
*/
public final static String UNSAFE_CHARS = "\"\\";
public BasicHeaderValueFormatter() {
super();
}
/**
* Formats an array of header elements.
*
* @param elems the header elements to format
* @param quote <code>true</code> to always format with quoted values,
* <code>false</code> to use quotes only when necessary
* @param formatter the formatter to use, or <code>null</code>
* for the {@link #INSTANCE default}
*
* @return the formatted header elements
*/
public static
String formatElements(final HeaderElement[] elems,
final boolean quote,
final HeaderValueFormatter formatter) {
return (formatter != null ? formatter : BasicHeaderValueFormatter.INSTANCE)
.formatElements(null, elems, quote).toString();
}
// non-javadoc, see interface HeaderValueFormatter
public CharArrayBuffer formatElements(final CharArrayBuffer charBuffer,
final HeaderElement[] elems,
final boolean quote) {
Args.notNull(elems, "Header element array");
final int len = estimateElementsLen(elems);
CharArrayBuffer buffer = charBuffer;
if (buffer == null) {
buffer = new CharArrayBuffer(len);
} else {
buffer.ensureCapacity(len);
}
for (int i=0; i<elems.length; i++) {
if (i > 0) {
buffer.append(", ");
}
formatHeaderElement(buffer, elems[i], quote);
}
return buffer;
}
/**
* Estimates the length of formatted header elements.
*
* @param elems the header elements to format, or <code>null</code>
*
* @return a length estimate, in number of characters
*/
protected int estimateElementsLen(final HeaderElement[] elems) {
if ((elems == null) || (elems.length < 1)) {
return 0;
}
int result = (elems.length-1) * 2; // elements separated by ", "
for (final HeaderElement elem : elems) {
result += estimateHeaderElementLen(elem);
}
return result;
}
/**
* Formats a header element.
*
* @param elem the header element to format
* @param quote <code>true</code> to always format with quoted values,
* <code>false</code> to use quotes only when necessary
* @param formatter the formatter to use, or <code>null</code>
* for the {@link #INSTANCE default}
*
* @return the formatted header element
*/
public static
String formatHeaderElement(final HeaderElement elem,
final boolean quote,
final HeaderValueFormatter formatter) {
return (formatter != null ? formatter : BasicHeaderValueFormatter.INSTANCE)
.formatHeaderElement(null, elem, quote).toString();
}
// non-javadoc, see interface HeaderValueFormatter
public CharArrayBuffer formatHeaderElement(final CharArrayBuffer charBuffer,
final HeaderElement elem,
final boolean quote) {
Args.notNull(elem, "Header element");
final int len = estimateHeaderElementLen(elem);
CharArrayBuffer buffer = charBuffer;
if (buffer == null) {
buffer = new CharArrayBuffer(len);
} else {
buffer.ensureCapacity(len);
}
buffer.append(elem.getName());
final String value = elem.getValue();
if (value != null) {
buffer.append('=');
doFormatValue(buffer, value, quote);
}
final int parcnt = elem.getParameterCount();
if (parcnt > 0) {
for (int i=0; i<parcnt; i++) {
buffer.append("; ");
formatNameValuePair(buffer, elem.getParameter(i), quote);
}
}
return buffer;
}
/**
* Estimates the length of a formatted header element.
*
* @param elem the header element to format, or <code>null</code>
*
* @return a length estimate, in number of characters
*/
protected int estimateHeaderElementLen(final HeaderElement elem) {
if (elem == null) {
return 0;
}
int result = elem.getName().length(); // name
final String value = elem.getValue();
if (value != null) {
// assume quotes, but no escaped characters
result += 3 + value.length(); // ="value"
}
final int parcnt = elem.getParameterCount();
if (parcnt > 0) {
for (int i=0; i<parcnt; i++) {
result += 2 + // ; <param>
estimateNameValuePairLen(elem.getParameter(i));
}
}
return result;
}
/**
* Formats a set of parameters.
*
* @param nvps the parameters to format
* @param quote <code>true</code> to always format with quoted values,
* <code>false</code> to use quotes only when necessary
* @param formatter the formatter to use, or <code>null</code>
* for the {@link #INSTANCE default}
*
* @return the formatted parameters
*/
public static
String formatParameters(final NameValuePair[] nvps,
final boolean quote,
final HeaderValueFormatter formatter) {
return (formatter != null ? formatter : BasicHeaderValueFormatter.INSTANCE)
.formatParameters(null, nvps, quote).toString();
}
// non-javadoc, see interface HeaderValueFormatter
public CharArrayBuffer formatParameters(final CharArrayBuffer charBuffer,
final NameValuePair[] nvps,
final boolean quote) {
Args.notNull(nvps, "Header parameter array");
final int len = estimateParametersLen(nvps);
CharArrayBuffer buffer = charBuffer;
if (buffer == null) {
buffer = new CharArrayBuffer(len);
} else {
buffer.ensureCapacity(len);
}
for (int i = 0; i < nvps.length; i++) {
if (i > 0) {
buffer.append("; ");
}
formatNameValuePair(buffer, nvps[i], quote);
}
return buffer;
}
/**
* Estimates the length of formatted parameters.
*
* @param nvps the parameters to format, or <code>null</code>
*
* @return a length estimate, in number of characters
*/
protected int estimateParametersLen(final NameValuePair[] nvps) {
if ((nvps == null) || (nvps.length < 1)) {
return 0;
}
int result = (nvps.length-1) * 2; // "; " between the parameters
for (final NameValuePair nvp : nvps) {
result += estimateNameValuePairLen(nvp);
}
return result;
}
/**
* Formats a name-value pair.
*
* @param nvp the name-value pair to format
* @param quote <code>true</code> to always format with a quoted value,
* <code>false</code> to use quotes only when necessary
* @param formatter the formatter to use, or <code>null</code>
* for the {@link #INSTANCE default}
*
* @return the formatted name-value pair
*/
public static
String formatNameValuePair(final NameValuePair nvp,
final boolean quote,
final HeaderValueFormatter formatter) {
return (formatter != null ? formatter : BasicHeaderValueFormatter.INSTANCE)
.formatNameValuePair(null, nvp, quote).toString();
}
// non-javadoc, see interface HeaderValueFormatter
public CharArrayBuffer formatNameValuePair(final CharArrayBuffer charBuffer,
final NameValuePair nvp,
final boolean quote) {
Args.notNull(nvp, "Name / value pair");
final int len = estimateNameValuePairLen(nvp);
CharArrayBuffer buffer = charBuffer;
if (buffer == null) {
buffer = new CharArrayBuffer(len);
} else {
buffer.ensureCapacity(len);
}
buffer.append(nvp.getName());
final String value = nvp.getValue();
if (value != null) {
buffer.append('=');
doFormatValue(buffer, value, quote);
}
return buffer;
}
/**
* Estimates the length of a formatted name-value pair.
*
* @param nvp the name-value pair to format, or <code>null</code>
*
* @return a length estimate, in number of characters
*/
protected int estimateNameValuePairLen(final NameValuePair nvp) {
if (nvp == null) {
return 0;
}
int result = nvp.getName().length(); // name
final String value = nvp.getValue();
if (value != null) {
// assume quotes, but no escaped characters
result += 3 + value.length(); // ="value"
}
return result;
}
/**
* Actually formats the value of a name-value pair.
* This does not include a leading = character.
* Called from {@link #formatNameValuePair formatNameValuePair}.
*
* @param buffer the buffer to append to, never <code>null</code>
* @param value the value to append, never <code>null</code>
* @param quote <code>true</code> to always format with quotes,
* <code>false</code> to use quotes only when necessary
*/
protected void doFormatValue(final CharArrayBuffer buffer,
final String value,
final boolean quote) {
boolean quoteFlag = quote;
if (!quoteFlag) {
for (int i = 0; (i < value.length()) && !quoteFlag; i++) {
quoteFlag = isSeparator(value.charAt(i));
}
}
if (quoteFlag) {
buffer.append('"');
}
for (int i = 0; i < value.length(); i++) {
final char ch = value.charAt(i);
if (isUnsafe(ch)) {
buffer.append('\\');
}
buffer.append(ch);
}
if (quoteFlag) {
buffer.append('"');
}
}
/**
* Checks whether a character is a {@link #SEPARATORS separator}.
*
* @param ch the character to check
*
* @return <code>true</code> if the character is a separator,
* <code>false</code> otherwise
*/
protected boolean isSeparator(final char ch) {
return SEPARATORS.indexOf(ch) >= 0;
}
/**
* Checks whether a character is {@link #UNSAFE_CHARS unsafe}.
*
* @param ch the character to check
*
* @return <code>true</code> if the character is unsafe,
* <code>false</code> otherwise
*/
protected boolean isUnsafe(final char ch) {
return UNSAFE_CHARS.indexOf(ch) >= 0;
}
} // class BasicHeaderValueFormatter

View file

@ -0,0 +1,359 @@
/*
* ====================================================================
* 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.message;
import java.util.ArrayList;
import java.util.List;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.NameValuePair;
import ch.boye.httpclientandroidlib.ParseException;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.protocol.HTTP;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* Basic implementation for parsing header values into elements.
* Instances of this class are stateless and thread-safe.
* Derived classes are expected to maintain these properties.
*
* @since 4.0
*/
@Immutable
public class BasicHeaderValueParser implements HeaderValueParser {
/**
* A default instance of this class, for use as default or fallback.
* Note that {@link BasicHeaderValueParser} is not a singleton, there
* can be many instances of the class itself and of derived classes.
* The instance here provides non-customized, default behavior.
*
* @deprecated (4.3) use {@link #INSTANCE}
*/
@Deprecated
public final static
BasicHeaderValueParser DEFAULT = new BasicHeaderValueParser();
public final static BasicHeaderValueParser INSTANCE = new BasicHeaderValueParser();
private final static char PARAM_DELIMITER = ';';
private final static char ELEM_DELIMITER = ',';
private final static char[] ALL_DELIMITERS = new char[] {
PARAM_DELIMITER,
ELEM_DELIMITER
};
public BasicHeaderValueParser() {
super();
}
/**
* Parses elements with the given parser.
*
* @param value the header value to parse
* @param parser the parser to use, or <code>null</code> for default
*
* @return array holding the header elements, never <code>null</code>
*/
public static
HeaderElement[] parseElements(final String value,
final HeaderValueParser parser) throws ParseException {
Args.notNull(value, "Value");
final CharArrayBuffer buffer = new CharArrayBuffer(value.length());
buffer.append(value);
final ParserCursor cursor = new ParserCursor(0, value.length());
return (parser != null ? parser : BasicHeaderValueParser.INSTANCE)
.parseElements(buffer, cursor);
}
// non-javadoc, see interface HeaderValueParser
public HeaderElement[] parseElements(final CharArrayBuffer buffer,
final ParserCursor cursor) {
Args.notNull(buffer, "Char array buffer");
Args.notNull(cursor, "Parser cursor");
final List<HeaderElement> elements = new ArrayList<HeaderElement>();
while (!cursor.atEnd()) {
final HeaderElement element = parseHeaderElement(buffer, cursor);
if (!(element.getName().length() == 0 && element.getValue() == null)) {
elements.add(element);
}
}
return elements.toArray(new HeaderElement[elements.size()]);
}
/**
* Parses an element with the given parser.
*
* @param value the header element to parse
* @param parser the parser to use, or <code>null</code> for default
*
* @return the parsed header element
*/
public static
HeaderElement parseHeaderElement(final String value,
final HeaderValueParser parser) throws ParseException {
Args.notNull(value, "Value");
final CharArrayBuffer buffer = new CharArrayBuffer(value.length());
buffer.append(value);
final ParserCursor cursor = new ParserCursor(0, value.length());
return (parser != null ? parser : BasicHeaderValueParser.INSTANCE)
.parseHeaderElement(buffer, cursor);
}
// non-javadoc, see interface HeaderValueParser
public HeaderElement parseHeaderElement(final CharArrayBuffer buffer,
final ParserCursor cursor) {
Args.notNull(buffer, "Char array buffer");
Args.notNull(cursor, "Parser cursor");
final NameValuePair nvp = parseNameValuePair(buffer, cursor);
NameValuePair[] params = null;
if (!cursor.atEnd()) {
final char ch = buffer.charAt(cursor.getPos() - 1);
if (ch != ELEM_DELIMITER) {
params = parseParameters(buffer, cursor);
}
}
return createHeaderElement(nvp.getName(), nvp.getValue(), params);
}
/**
* Creates a header element.
* Called from {@link #parseHeaderElement}.
*
* @return a header element representing the argument
*/
protected HeaderElement createHeaderElement(
final String name,
final String value,
final NameValuePair[] params) {
return new BasicHeaderElement(name, value, params);
}
/**
* Parses parameters with the given parser.
*
* @param value the parameter list to parse
* @param parser the parser to use, or <code>null</code> for default
*
* @return array holding the parameters, never <code>null</code>
*/
public static
NameValuePair[] parseParameters(final String value,
final HeaderValueParser parser) throws ParseException {
Args.notNull(value, "Value");
final CharArrayBuffer buffer = new CharArrayBuffer(value.length());
buffer.append(value);
final ParserCursor cursor = new ParserCursor(0, value.length());
return (parser != null ? parser : BasicHeaderValueParser.INSTANCE)
.parseParameters(buffer, cursor);
}
// non-javadoc, see interface HeaderValueParser
public NameValuePair[] parseParameters(final CharArrayBuffer buffer,
final ParserCursor cursor) {
Args.notNull(buffer, "Char array buffer");
Args.notNull(cursor, "Parser cursor");
int pos = cursor.getPos();
final int indexTo = cursor.getUpperBound();
while (pos < indexTo) {
final char ch = buffer.charAt(pos);
if (HTTP.isWhitespace(ch)) {
pos++;
} else {
break;
}
}
cursor.updatePos(pos);
if (cursor.atEnd()) {
return new NameValuePair[] {};
}
final List<NameValuePair> params = new ArrayList<NameValuePair>();
while (!cursor.atEnd()) {
final NameValuePair param = parseNameValuePair(buffer, cursor);
params.add(param);
final char ch = buffer.charAt(cursor.getPos() - 1);
if (ch == ELEM_DELIMITER) {
break;
}
}
return params.toArray(new NameValuePair[params.size()]);
}
/**
* Parses a name-value-pair with the given parser.
*
* @param value the NVP to parse
* @param parser the parser to use, or <code>null</code> for default
*
* @return the parsed name-value pair
*/
public static
NameValuePair parseNameValuePair(final String value,
final HeaderValueParser parser) throws ParseException {
Args.notNull(value, "Value");
final CharArrayBuffer buffer = new CharArrayBuffer(value.length());
buffer.append(value);
final ParserCursor cursor = new ParserCursor(0, value.length());
return (parser != null ? parser : BasicHeaderValueParser.INSTANCE)
.parseNameValuePair(buffer, cursor);
}
// non-javadoc, see interface HeaderValueParser
public NameValuePair parseNameValuePair(final CharArrayBuffer buffer,
final ParserCursor cursor) {
return parseNameValuePair(buffer, cursor, ALL_DELIMITERS);
}
private static boolean isOneOf(final char ch, final char[] chs) {
if (chs != null) {
for (final char ch2 : chs) {
if (ch == ch2) {
return true;
}
}
}
return false;
}
public NameValuePair parseNameValuePair(final CharArrayBuffer buffer,
final ParserCursor cursor,
final char[] delimiters) {
Args.notNull(buffer, "Char array buffer");
Args.notNull(cursor, "Parser cursor");
boolean terminated = false;
int pos = cursor.getPos();
final int indexFrom = cursor.getPos();
final int indexTo = cursor.getUpperBound();
// Find name
final String name;
while (pos < indexTo) {
final char ch = buffer.charAt(pos);
if (ch == '=') {
break;
}
if (isOneOf(ch, delimiters)) {
terminated = true;
break;
}
pos++;
}
if (pos == indexTo) {
terminated = true;
name = buffer.substringTrimmed(indexFrom, indexTo);
} else {
name = buffer.substringTrimmed(indexFrom, pos);
pos++;
}
if (terminated) {
cursor.updatePos(pos);
return createNameValuePair(name, null);
}
// Find value
final String value;
int i1 = pos;
boolean qouted = false;
boolean escaped = false;
while (pos < indexTo) {
final char ch = buffer.charAt(pos);
if (ch == '"' && !escaped) {
qouted = !qouted;
}
if (!qouted && !escaped && isOneOf(ch, delimiters)) {
terminated = true;
break;
}
if (escaped) {
escaped = false;
} else {
escaped = qouted && ch == '\\';
}
pos++;
}
int i2 = pos;
// Trim leading white spaces
while (i1 < i2 && (HTTP.isWhitespace(buffer.charAt(i1)))) {
i1++;
}
// Trim trailing white spaces
while ((i2 > i1) && (HTTP.isWhitespace(buffer.charAt(i2 - 1)))) {
i2--;
}
// Strip away quotes if necessary
if (((i2 - i1) >= 2)
&& (buffer.charAt(i1) == '"')
&& (buffer.charAt(i2 - 1) == '"')) {
i1++;
i2--;
}
value = buffer.substring(i1, i2);
if (terminated) {
pos++;
}
cursor.updatePos(pos);
return createNameValuePair(name, value);
}
/**
* Creates a name-value pair.
* Called from {@link #parseNameValuePair}.
*
* @param name the name
* @param value the value, or <code>null</code>
*
* @return a name-value pair representing the arguments
*/
protected NameValuePair createNameValuePair(final String name, final String value) {
return new BasicNameValuePair(name, value);
}
}

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.message;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest;
import ch.boye.httpclientandroidlib.ProtocolVersion;
import ch.boye.httpclientandroidlib.RequestLine;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.protocol.HTTP;
/**
* Basic implementation of {@link HttpEntityEnclosingRequest}.
*
* @since 4.0
*/
@NotThreadSafe
public class BasicHttpEntityEnclosingRequest
extends BasicHttpRequest implements HttpEntityEnclosingRequest {
private HttpEntity entity;
public BasicHttpEntityEnclosingRequest(final String method, final String uri) {
super(method, uri);
}
public BasicHttpEntityEnclosingRequest(final String method, final String uri,
final ProtocolVersion ver) {
super(method, uri, ver);
}
public BasicHttpEntityEnclosingRequest(final RequestLine requestline) {
super(requestline);
}
public HttpEntity getEntity() {
return this.entity;
}
public void setEntity(final HttpEntity entity) {
this.entity = entity;
}
public boolean expectContinue() {
final Header expect = getFirstHeader(HTTP.EXPECT_DIRECTIVE);
return expect != null && HTTP.EXPECT_CONTINUE.equalsIgnoreCase(expect.getValue());
}
}

View file

@ -0,0 +1,114 @@
/*
* ====================================================================
* 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.message;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpVersion;
import ch.boye.httpclientandroidlib.ProtocolVersion;
import ch.boye.httpclientandroidlib.RequestLine;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Basic implementation of {@link HttpRequest}.
*
* @since 4.0
*/
@NotThreadSafe
public class BasicHttpRequest extends AbstractHttpMessage implements HttpRequest {
private final String method;
private final String uri;
private RequestLine requestline;
/**
* Creates an instance of this class using the given request method
* and URI.
*
* @param method request method.
* @param uri request URI.
*/
public BasicHttpRequest(final String method, final String uri) {
super();
this.method = Args.notNull(method, "Method name");
this.uri = Args.notNull(uri, "Request URI");
this.requestline = null;
}
/**
* Creates an instance of this class using the given request method, URI
* and the HTTP protocol version.
*
* @param method request method.
* @param uri request URI.
* @param ver HTTP protocol version.
*/
public BasicHttpRequest(final String method, final String uri, final ProtocolVersion ver) {
this(new BasicRequestLine(method, uri, ver));
}
/**
* Creates an instance of this class using the given request line.
*
* @param requestline request line.
*/
public BasicHttpRequest(final RequestLine requestline) {
super();
this.requestline = Args.notNull(requestline, "Request line");
this.method = requestline.getMethod();
this.uri = requestline.getUri();
}
/**
* Returns the HTTP protocol version to be used for this request.
*
* @see #BasicHttpRequest(String, String)
*/
public ProtocolVersion getProtocolVersion() {
return getRequestLine().getProtocolVersion();
}
/**
* Returns the request line of this request.
*
* @see #BasicHttpRequest(String, String)
*/
public RequestLine getRequestLine() {
if (this.requestline == null) {
this.requestline = new BasicRequestLine(this.method, this.uri, HttpVersion.HTTP_1_1);
}
return this.requestline;
}
@Override
public String toString() {
return this.method + " " + this.uri + " " + this.headergroup;
}
}

View file

@ -0,0 +1,218 @@
/*
* ====================================================================
* 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.message;
import java.util.Locale;
import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpVersion;
import ch.boye.httpclientandroidlib.ProtocolVersion;
import ch.boye.httpclientandroidlib.ReasonPhraseCatalog;
import ch.boye.httpclientandroidlib.StatusLine;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Basic implementation of {@link HttpResponse}.
*
* @see ch.boye.httpclientandroidlib.impl.DefaultHttpResponseFactory
*
* @since 4.0
*/
@NotThreadSafe
public class BasicHttpResponse extends AbstractHttpMessage implements HttpResponse {
private StatusLine statusline;
private ProtocolVersion ver;
private int code;
private String reasonPhrase;
private HttpEntity entity;
private final ReasonPhraseCatalog reasonCatalog;
private Locale locale;
/**
* Creates a new response.
* This is the constructor to which all others map.
*
* @param statusline the status line
* @param catalog the reason phrase catalog, or
* <code>null</code> to disable automatic
* reason phrase lookup
* @param locale the locale for looking up reason phrases, or
* <code>null</code> for the system locale
*/
public BasicHttpResponse(final StatusLine statusline,
final ReasonPhraseCatalog catalog,
final Locale locale) {
super();
this.statusline = Args.notNull(statusline, "Status line");
this.ver = statusline.getProtocolVersion();
this.code = statusline.getStatusCode();
this.reasonPhrase = statusline.getReasonPhrase();
this.reasonCatalog = catalog;
this.locale = locale;
}
/**
* Creates a response from a status line.
* The response will not have a reason phrase catalog and
* use the system default locale.
*
* @param statusline the status line
*/
public BasicHttpResponse(final StatusLine statusline) {
super();
this.statusline = Args.notNull(statusline, "Status line");
this.ver = statusline.getProtocolVersion();
this.code = statusline.getStatusCode();
this.reasonPhrase = statusline.getReasonPhrase();
this.reasonCatalog = null;
this.locale = null;
}
/**
* Creates a response from elements of a status line.
* The response will not have a reason phrase catalog and
* use the system default locale.
*
* @param ver the protocol version of the response
* @param code the status code of the response
* @param reason the reason phrase to the status code, or
* <code>null</code>
*/
public BasicHttpResponse(final ProtocolVersion ver,
final int code,
final String reason) {
super();
Args.notNegative(code, "Status code");
this.statusline = null;
this.ver = ver;
this.code = code;
this.reasonPhrase = reason;
this.reasonCatalog = null;
this.locale = null;
}
// non-javadoc, see interface HttpMessage
public ProtocolVersion getProtocolVersion() {
return this.ver;
}
// non-javadoc, see interface HttpResponse
public StatusLine getStatusLine() {
if (this.statusline == null) {
this.statusline = new BasicStatusLine(
this.ver != null ? this.ver : HttpVersion.HTTP_1_1,
this.code,
this.reasonPhrase != null ? this.reasonPhrase : getReason(this.code));
}
return this.statusline;
}
// non-javadoc, see interface HttpResponse
public HttpEntity getEntity() {
return this.entity;
}
public Locale getLocale() {
return this.locale;
}
// non-javadoc, see interface HttpResponse
public void setStatusLine(final StatusLine statusline) {
this.statusline = Args.notNull(statusline, "Status line");
this.ver = statusline.getProtocolVersion();
this.code = statusline.getStatusCode();
this.reasonPhrase = statusline.getReasonPhrase();
}
// non-javadoc, see interface HttpResponse
public void setStatusLine(final ProtocolVersion ver, final int code) {
Args.notNegative(code, "Status code");
this.statusline = null;
this.ver = ver;
this.code = code;
this.reasonPhrase = null;
}
// non-javadoc, see interface HttpResponse
public void setStatusLine(
final ProtocolVersion ver, final int code, final String reason) {
Args.notNegative(code, "Status code");
this.statusline = null;
this.ver = ver;
this.code = code;
this.reasonPhrase = reason;
}
// non-javadoc, see interface HttpResponse
public void setStatusCode(final int code) {
Args.notNegative(code, "Status code");
this.statusline = null;
this.code = code;
this.reasonPhrase = null;
}
// non-javadoc, see interface HttpResponse
public void setReasonPhrase(final String reason) {
this.statusline = null;
this.reasonPhrase = reason;
}
// non-javadoc, see interface HttpResponse
public void setEntity(final HttpEntity entity) {
this.entity = entity;
}
public void setLocale(final Locale locale) {
this.locale = Args.notNull(locale, "Locale");
this.statusline = null;
}
/**
* Looks up a reason phrase.
* This method evaluates the currently set catalog and locale.
* It also handles a missing catalog.
*
* @param code the status code for which to look up the reason
*
* @return the reason phrase, or <code>null</code> if there is none
*/
protected String getReason(final int code) {
return this.reasonCatalog != null ? this.reasonCatalog.getReason(code,
this.locale != null ? this.locale : Locale.getDefault()) : null;
}
@Override
public String toString() {
return getStatusLine() + " " + this.headergroup;
}
}

View file

@ -0,0 +1,319 @@
/*
* ====================================================================
* 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.message;
import ch.boye.httpclientandroidlib.FormattedHeader;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.ProtocolVersion;
import ch.boye.httpclientandroidlib.RequestLine;
import ch.boye.httpclientandroidlib.StatusLine;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* Interface for formatting elements of the HEAD section of an HTTP message.
* This is the complement to {@link LineParser}.
* There are individual methods for formatting a request line, a
* status line, or a header line. The formatting does <i>not</i> include the
* trailing line break sequence CR-LF.
* The formatted lines are returned in memory, the formatter does not depend
* on any specific IO mechanism.
* Instances of this interface are expected to be stateless and thread-safe.
*
* @since 4.0
*/
@Immutable
public class BasicLineFormatter implements LineFormatter {
/**
* A default instance of this class, for use as default or fallback.
* Note that {@link BasicLineFormatter} is not a singleton, there can
* be many instances of the class itself and of derived classes.
* The instance here provides non-customized, default behavior.
*
* @deprecated (4.3) use {@link #INSTANCE}
*/
@Deprecated
public final static BasicLineFormatter DEFAULT = new BasicLineFormatter();
public final static BasicLineFormatter INSTANCE = new BasicLineFormatter();
public BasicLineFormatter() {
super();
}
/**
* Obtains a buffer for formatting.
*
* @param charBuffer a buffer already available, or <code>null</code>
*
* @return the cleared argument buffer if there is one, or
* a new empty buffer that can be used for formatting
*/
protected CharArrayBuffer initBuffer(final CharArrayBuffer charBuffer) {
CharArrayBuffer buffer = charBuffer;
if (buffer != null) {
buffer.clear();
} else {
buffer = new CharArrayBuffer(64);
}
return buffer;
}
/**
* Formats a protocol version.
*
* @param version the protocol version to format
* @param formatter the formatter to use, or
* <code>null</code> for the
* {@link #INSTANCE default}
*
* @return the formatted protocol version
*/
public static
String formatProtocolVersion(final ProtocolVersion version,
final LineFormatter formatter) {
return (formatter != null ? formatter : BasicLineFormatter.INSTANCE)
.appendProtocolVersion(null, version).toString();
}
// non-javadoc, see interface LineFormatter
public CharArrayBuffer appendProtocolVersion(final CharArrayBuffer buffer,
final ProtocolVersion version) {
Args.notNull(version, "Protocol version");
// can't use initBuffer, that would clear the argument!
CharArrayBuffer result = buffer;
final int len = estimateProtocolVersionLen(version);
if (result == null) {
result = new CharArrayBuffer(len);
} else {
result.ensureCapacity(len);
}
result.append(version.getProtocol());
result.append('/');
result.append(Integer.toString(version.getMajor()));
result.append('.');
result.append(Integer.toString(version.getMinor()));
return result;
}
/**
* Guesses the length of a formatted protocol version.
* Needed to guess the length of a formatted request or status line.
*
* @param version the protocol version to format, or <code>null</code>
*
* @return the estimated length of the formatted protocol version,
* in characters
*/
protected int estimateProtocolVersionLen(final ProtocolVersion version) {
return version.getProtocol().length() + 4; // room for "HTTP/1.1"
}
/**
* Formats a request line.
*
* @param reqline the request line to format
* @param formatter the formatter to use, or
* <code>null</code> for the
* {@link #INSTANCE default}
*
* @return the formatted request line
*/
public static String formatRequestLine(final RequestLine reqline,
final LineFormatter formatter) {
return (formatter != null ? formatter : BasicLineFormatter.INSTANCE)
.formatRequestLine(null, reqline).toString();
}
// non-javadoc, see interface LineFormatter
public CharArrayBuffer formatRequestLine(final CharArrayBuffer buffer,
final RequestLine reqline) {
Args.notNull(reqline, "Request line");
final CharArrayBuffer result = initBuffer(buffer);
doFormatRequestLine(result, reqline);
return result;
}
/**
* Actually formats a request line.
* Called from {@link #formatRequestLine}.
*
* @param buffer the empty buffer into which to format,
* never <code>null</code>
* @param reqline the request line to format, never <code>null</code>
*/
protected void doFormatRequestLine(final CharArrayBuffer buffer,
final RequestLine reqline) {
final String method = reqline.getMethod();
final String uri = reqline.getUri();
// room for "GET /index.html HTTP/1.1"
final int len = method.length() + 1 + uri.length() + 1 +
estimateProtocolVersionLen(reqline.getProtocolVersion());
buffer.ensureCapacity(len);
buffer.append(method);
buffer.append(' ');
buffer.append(uri);
buffer.append(' ');
appendProtocolVersion(buffer, reqline.getProtocolVersion());
}
/**
* Formats a status line.
*
* @param statline the status line to format
* @param formatter the formatter to use, or
* <code>null</code> for the
* {@link #INSTANCE default}
*
* @return the formatted status line
*/
public static String formatStatusLine(final StatusLine statline,
final LineFormatter formatter) {
return (formatter != null ? formatter : BasicLineFormatter.INSTANCE)
.formatStatusLine(null, statline).toString();
}
// non-javadoc, see interface LineFormatter
public CharArrayBuffer formatStatusLine(final CharArrayBuffer buffer,
final StatusLine statline) {
Args.notNull(statline, "Status line");
final CharArrayBuffer result = initBuffer(buffer);
doFormatStatusLine(result, statline);
return result;
}
/**
* Actually formats a status line.
* Called from {@link #formatStatusLine}.
*
* @param buffer the empty buffer into which to format,
* never <code>null</code>
* @param statline the status line to format, never <code>null</code>
*/
protected void doFormatStatusLine(final CharArrayBuffer buffer,
final StatusLine statline) {
int len = estimateProtocolVersionLen(statline.getProtocolVersion())
+ 1 + 3 + 1; // room for "HTTP/1.1 200 "
final String reason = statline.getReasonPhrase();
if (reason != null) {
len += reason.length();
}
buffer.ensureCapacity(len);
appendProtocolVersion(buffer, statline.getProtocolVersion());
buffer.append(' ');
buffer.append(Integer.toString(statline.getStatusCode()));
buffer.append(' '); // keep whitespace even if reason phrase is empty
if (reason != null) {
buffer.append(reason);
}
}
/**
* Formats a header.
*
* @param header the header to format
* @param formatter the formatter to use, or
* <code>null</code> for the
* {@link #INSTANCE default}
*
* @return the formatted header
*/
public static String formatHeader(final Header header,
final LineFormatter formatter) {
return (formatter != null ? formatter : BasicLineFormatter.INSTANCE)
.formatHeader(null, header).toString();
}
// non-javadoc, see interface LineFormatter
public CharArrayBuffer formatHeader(final CharArrayBuffer buffer,
final Header header) {
Args.notNull(header, "Header");
final CharArrayBuffer result;
if (header instanceof FormattedHeader) {
// If the header is backed by a buffer, re-use the buffer
result = ((FormattedHeader)header).getBuffer();
} else {
result = initBuffer(buffer);
doFormatHeader(result, header);
}
return result;
} // formatHeader
/**
* Actually formats a header.
* Called from {@link #formatHeader}.
*
* @param buffer the empty buffer into which to format,
* never <code>null</code>
* @param header the header to format, never <code>null</code>
*/
protected void doFormatHeader(final CharArrayBuffer buffer,
final Header header) {
final String name = header.getName();
final String value = header.getValue();
int len = name.length() + 2;
if (value != null) {
len += value.length();
}
buffer.ensureCapacity(len);
buffer.append(name);
buffer.append(": ");
if (value != null) {
buffer.append(value);
}
}
} // class BasicLineFormatter

View file

@ -0,0 +1,456 @@
/*
* ====================================================================
* 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.message;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpVersion;
import ch.boye.httpclientandroidlib.ParseException;
import ch.boye.httpclientandroidlib.ProtocolVersion;
import ch.boye.httpclientandroidlib.RequestLine;
import ch.boye.httpclientandroidlib.StatusLine;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.protocol.HTTP;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* Basic parser for lines in the head section of an HTTP message.
* There are individual methods for parsing a request line, a
* status line, or a header line.
* The lines to parse are passed in memory, the parser does not depend
* on any specific IO mechanism.
* Instances of this class are stateless and thread-safe.
* Derived classes MUST maintain these properties.
*
* <p>
* Note: This class was created by refactoring parsing code located in
* various other classes. The author tags from those other classes have
* been replicated here, although the association with the parsing code
* taken from there has not been traced.
* </p>
*
* @since 4.0
*/
@Immutable
public class BasicLineParser implements LineParser {
/**
* A default instance of this class, for use as default or fallback.
* Note that {@link BasicLineParser} is not a singleton, there can
* be many instances of the class itself and of derived classes.
* The instance here provides non-customized, default behavior.
*
* @deprecated (4.3) use {@link #INSTANCE}
*/
@Deprecated
public final static BasicLineParser DEFAULT = new BasicLineParser();
public final static BasicLineParser INSTANCE = new BasicLineParser();
/**
* A version of the protocol to parse.
* The version is typically not relevant, but the protocol name.
*/
protected final ProtocolVersion protocol;
/**
* Creates a new line parser for the given HTTP-like protocol.
*
* @param proto a version of the protocol to parse, or
* <code>null</code> for HTTP. The actual version
* is not relevant, only the protocol name.
*/
public BasicLineParser(final ProtocolVersion proto) {
this.protocol = proto != null? proto : HttpVersion.HTTP_1_1;
}
/**
* Creates a new line parser for HTTP.
*/
public BasicLineParser() {
this(null);
}
public static
ProtocolVersion parseProtocolVersion(final String value,
final LineParser parser) throws ParseException {
Args.notNull(value, "Value");
final CharArrayBuffer buffer = new CharArrayBuffer(value.length());
buffer.append(value);
final ParserCursor cursor = new ParserCursor(0, value.length());
return (parser != null ? parser : BasicLineParser.INSTANCE)
.parseProtocolVersion(buffer, cursor);
}
// non-javadoc, see interface LineParser
public ProtocolVersion parseProtocolVersion(final CharArrayBuffer buffer,
final ParserCursor cursor) throws ParseException {
Args.notNull(buffer, "Char array buffer");
Args.notNull(cursor, "Parser cursor");
final String protoname = this.protocol.getProtocol();
final int protolength = protoname.length();
final int indexFrom = cursor.getPos();
final int indexTo = cursor.getUpperBound();
skipWhitespace(buffer, cursor);
int i = cursor.getPos();
// long enough for "HTTP/1.1"?
if (i + protolength + 4 > indexTo) {
throw new ParseException
("Not a valid protocol version: " +
buffer.substring(indexFrom, indexTo));
}
// check the protocol name and slash
boolean ok = true;
for (int j=0; ok && (j<protolength); j++) {
ok = (buffer.charAt(i+j) == protoname.charAt(j));
}
if (ok) {
ok = (buffer.charAt(i+protolength) == '/');
}
if (!ok) {
throw new ParseException
("Not a valid protocol version: " +
buffer.substring(indexFrom, indexTo));
}
i += protolength+1;
final int period = buffer.indexOf('.', i, indexTo);
if (period == -1) {
throw new ParseException
("Invalid protocol version number: " +
buffer.substring(indexFrom, indexTo));
}
final int major;
try {
major = Integer.parseInt(buffer.substringTrimmed(i, period));
} catch (final NumberFormatException e) {
throw new ParseException
("Invalid protocol major version number: " +
buffer.substring(indexFrom, indexTo));
}
i = period + 1;
int blank = buffer.indexOf(' ', i, indexTo);
if (blank == -1) {
blank = indexTo;
}
final int minor;
try {
minor = Integer.parseInt(buffer.substringTrimmed(i, blank));
} catch (final NumberFormatException e) {
throw new ParseException(
"Invalid protocol minor version number: " +
buffer.substring(indexFrom, indexTo));
}
cursor.updatePos(blank);
return createProtocolVersion(major, minor);
} // parseProtocolVersion
/**
* Creates a protocol version.
* Called from {@link #parseProtocolVersion}.
*
* @param major the major version number, for example 1 in HTTP/1.0
* @param minor the minor version number, for example 0 in HTTP/1.0
*
* @return the protocol version
*/
protected ProtocolVersion createProtocolVersion(final int major, final int minor) {
return protocol.forVersion(major, minor);
}
// non-javadoc, see interface LineParser
public boolean hasProtocolVersion(final CharArrayBuffer buffer,
final ParserCursor cursor) {
Args.notNull(buffer, "Char array buffer");
Args.notNull(cursor, "Parser cursor");
int index = cursor.getPos();
final String protoname = this.protocol.getProtocol();
final int protolength = protoname.length();
if (buffer.length() < protolength+4)
{
return false; // not long enough for "HTTP/1.1"
}
if (index < 0) {
// end of line, no tolerance for trailing whitespace
// this works only for single-digit major and minor version
index = buffer.length() -4 -protolength;
} else if (index == 0) {
// beginning of line, tolerate leading whitespace
while ((index < buffer.length()) &&
HTTP.isWhitespace(buffer.charAt(index))) {
index++;
}
} // else within line, don't tolerate whitespace
if (index + protolength + 4 > buffer.length()) {
return false;
}
// just check protocol name and slash, no need to analyse the version
boolean ok = true;
for (int j=0; ok && (j<protolength); j++) {
ok = (buffer.charAt(index+j) == protoname.charAt(j));
}
if (ok) {
ok = (buffer.charAt(index+protolength) == '/');
}
return ok;
}
public static
RequestLine parseRequestLine(final String value,
final LineParser parser) throws ParseException {
Args.notNull(value, "Value");
final CharArrayBuffer buffer = new CharArrayBuffer(value.length());
buffer.append(value);
final ParserCursor cursor = new ParserCursor(0, value.length());
return (parser != null ? parser : BasicLineParser.INSTANCE)
.parseRequestLine(buffer, cursor);
}
/**
* Parses a request line.
*
* @param buffer a buffer holding the line to parse
*
* @return the parsed request line
*
* @throws ParseException in case of a parse error
*/
public RequestLine parseRequestLine(final CharArrayBuffer buffer,
final ParserCursor cursor) throws ParseException {
Args.notNull(buffer, "Char array buffer");
Args.notNull(cursor, "Parser cursor");
final int indexFrom = cursor.getPos();
final int indexTo = cursor.getUpperBound();
try {
skipWhitespace(buffer, cursor);
int i = cursor.getPos();
int blank = buffer.indexOf(' ', i, indexTo);
if (blank < 0) {
throw new ParseException("Invalid request line: " +
buffer.substring(indexFrom, indexTo));
}
final String method = buffer.substringTrimmed(i, blank);
cursor.updatePos(blank);
skipWhitespace(buffer, cursor);
i = cursor.getPos();
blank = buffer.indexOf(' ', i, indexTo);
if (blank < 0) {
throw new ParseException("Invalid request line: " +
buffer.substring(indexFrom, indexTo));
}
final String uri = buffer.substringTrimmed(i, blank);
cursor.updatePos(blank);
final ProtocolVersion ver = parseProtocolVersion(buffer, cursor);
skipWhitespace(buffer, cursor);
if (!cursor.atEnd()) {
throw new ParseException("Invalid request line: " +
buffer.substring(indexFrom, indexTo));
}
return createRequestLine(method, uri, ver);
} catch (final IndexOutOfBoundsException e) {
throw new ParseException("Invalid request line: " +
buffer.substring(indexFrom, indexTo));
}
} // parseRequestLine
/**
* Instantiates a new request line.
* Called from {@link #parseRequestLine}.
*
* @param method the request method
* @param uri the requested URI
* @param ver the protocol version
*
* @return a new status line with the given data
*/
protected RequestLine createRequestLine(final String method,
final String uri,
final ProtocolVersion ver) {
return new BasicRequestLine(method, uri, ver);
}
public static
StatusLine parseStatusLine(final String value,
final LineParser parser) throws ParseException {
Args.notNull(value, "Value");
final CharArrayBuffer buffer = new CharArrayBuffer(value.length());
buffer.append(value);
final ParserCursor cursor = new ParserCursor(0, value.length());
return (parser != null ? parser : BasicLineParser.INSTANCE)
.parseStatusLine(buffer, cursor);
}
// non-javadoc, see interface LineParser
public StatusLine parseStatusLine(final CharArrayBuffer buffer,
final ParserCursor cursor) throws ParseException {
Args.notNull(buffer, "Char array buffer");
Args.notNull(cursor, "Parser cursor");
final int indexFrom = cursor.getPos();
final int indexTo = cursor.getUpperBound();
try {
// handle the HTTP-Version
final ProtocolVersion ver = parseProtocolVersion(buffer, cursor);
// handle the Status-Code
skipWhitespace(buffer, cursor);
int i = cursor.getPos();
int blank = buffer.indexOf(' ', i, indexTo);
if (blank < 0) {
blank = indexTo;
}
final int statusCode;
final String s = buffer.substringTrimmed(i, blank);
for (int j = 0; j < s.length(); j++) {
if (!Character.isDigit(s.charAt(j))) {
throw new ParseException(
"Status line contains invalid status code: "
+ buffer.substring(indexFrom, indexTo));
}
}
try {
statusCode = Integer.parseInt(s);
} catch (final NumberFormatException e) {
throw new ParseException(
"Status line contains invalid status code: "
+ buffer.substring(indexFrom, indexTo));
}
//handle the Reason-Phrase
i = blank;
final String reasonPhrase;
if (i < indexTo) {
reasonPhrase = buffer.substringTrimmed(i, indexTo);
} else {
reasonPhrase = "";
}
return createStatusLine(ver, statusCode, reasonPhrase);
} catch (final IndexOutOfBoundsException e) {
throw new ParseException("Invalid status line: " +
buffer.substring(indexFrom, indexTo));
}
} // parseStatusLine
/**
* Instantiates a new status line.
* Called from {@link #parseStatusLine}.
*
* @param ver the protocol version
* @param status the status code
* @param reason the reason phrase
*
* @return a new status line with the given data
*/
protected StatusLine createStatusLine(final ProtocolVersion ver,
final int status,
final String reason) {
return new BasicStatusLine(ver, status, reason);
}
public static
Header parseHeader(final String value,
final LineParser parser) throws ParseException {
Args.notNull(value, "Value");
final CharArrayBuffer buffer = new CharArrayBuffer(value.length());
buffer.append(value);
return (parser != null ? parser : BasicLineParser.INSTANCE)
.parseHeader(buffer);
}
// non-javadoc, see interface LineParser
public Header parseHeader(final CharArrayBuffer buffer)
throws ParseException {
// the actual parser code is in the constructor of BufferedHeader
return new BufferedHeader(buffer);
}
/**
* Helper to skip whitespace.
*/
protected void skipWhitespace(final CharArrayBuffer buffer, final ParserCursor cursor) {
int pos = cursor.getPos();
final int indexTo = cursor.getUpperBound();
while ((pos < indexTo) &&
HTTP.isWhitespace(buffer.charAt(pos))) {
pos++;
}
cursor.updatePos(pos);
}
} // class BasicLineParser

View file

@ -0,0 +1,190 @@
/*
* ====================================================================
* 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.message;
import java.util.List;
import java.util.NoSuchElementException;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HeaderIterator;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.Asserts;
/**
* Implementation of a {@link HeaderIterator} based on a {@link List}.
* For use by {@link HeaderGroup}.
*
* @since 4.0
*/
@NotThreadSafe
public class BasicListHeaderIterator implements HeaderIterator {
/**
* A list of headers to iterate over.
* Not all elements of this array are necessarily part of the iteration.
*/
protected final List<Header> allHeaders;
/**
* The position of the next header in {@link #allHeaders allHeaders}.
* Negative if the iteration is over.
*/
protected int currentIndex;
/**
* The position of the last returned header.
* Negative if none has been returned so far.
*/
protected int lastIndex;
/**
* The header name to filter by.
* <code>null</code> to iterate over all headers in the array.
*/
protected String headerName;
/**
* Creates a new header iterator.
*
* @param headers a list of headers over which to iterate
* @param name the name of the headers over which to iterate, or
* <code>null</code> for any
*/
public BasicListHeaderIterator(final List<Header> headers, final String name) {
super();
this.allHeaders = Args.notNull(headers, "Header list");
this.headerName = name;
this.currentIndex = findNext(-1);
this.lastIndex = -1;
}
/**
* Determines the index of the next header.
*
* @param pos one less than the index to consider first,
* -1 to search for the first header
*
* @return the index of the next header that matches the filter name,
* or negative if there are no more headers
*/
protected int findNext(final int pos) {
int from = pos;
if (from < -1) {
return -1;
}
final int to = this.allHeaders.size()-1;
boolean found = false;
while (!found && (from < to)) {
from++;
found = filterHeader(from);
}
return found ? from : -1;
}
/**
* Checks whether a header is part of the iteration.
*
* @param index the index of the header to check
*
* @return <code>true</code> if the header should be part of the
* iteration, <code>false</code> to skip
*/
protected boolean filterHeader(final int index) {
if (this.headerName == null) {
return true;
}
// non-header elements, including null, will trigger exceptions
final String name = (this.allHeaders.get(index)).getName();
return this.headerName.equalsIgnoreCase(name);
}
// non-javadoc, see interface HeaderIterator
public boolean hasNext() {
return (this.currentIndex >= 0);
}
/**
* Obtains the next header from this iteration.
*
* @return the next header in this iteration
*
* @throws NoSuchElementException if there are no more headers
*/
public Header nextHeader()
throws NoSuchElementException {
final int current = this.currentIndex;
if (current < 0) {
throw new NoSuchElementException("Iteration already finished.");
}
this.lastIndex = current;
this.currentIndex = findNext(current);
return this.allHeaders.get(current);
}
/**
* Returns the next header.
* Same as {@link #nextHeader nextHeader}, but not type-safe.
*
* @return the next header in this iteration
*
* @throws NoSuchElementException if there are no more headers
*/
public final Object next()
throws NoSuchElementException {
return nextHeader();
}
/**
* Removes the header that was returned last.
*/
public void remove()
throws UnsupportedOperationException {
Asserts.check(this.lastIndex >= 0, "No header to remove");
this.allHeaders.remove(this.lastIndex);
this.lastIndex = -1;
this.currentIndex--; // adjust for the removed element
}
}

View file

@ -0,0 +1,111 @@
/*
* ====================================================================
* 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.message;
import java.io.Serializable;
import ch.boye.httpclientandroidlib.NameValuePair;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.LangUtils;
/**
* Basic implementation of {@link NameValuePair}.
*
* @since 4.0
*/
@Immutable
public class BasicNameValuePair implements NameValuePair, Cloneable, Serializable {
private static final long serialVersionUID = -6437800749411518984L;
private final String name;
private final String value;
/**
* Default Constructor taking a name and a value. The value may be null.
*
* @param name The name.
* @param value The value.
*/
public BasicNameValuePair(final String name, final String value) {
super();
this.name = Args.notNull(name, "Name");
this.value = value;
}
public String getName() {
return this.name;
}
public String getValue() {
return this.value;
}
@Override
public String toString() {
// don't call complex default formatting for a simple toString
if (this.value == null) {
return name;
}
final int len = this.name.length() + 1 + this.value.length();
final StringBuilder buffer = new StringBuilder(len);
buffer.append(this.name);
buffer.append("=");
buffer.append(this.value);
return buffer.toString();
}
@Override
public boolean equals(final Object object) {
if (this == object) {
return true;
}
if (object instanceof NameValuePair) {
final BasicNameValuePair that = (BasicNameValuePair) object;
return this.name.equals(that.name)
&& LangUtils.equals(this.value, that.value);
}
return false;
}
@Override
public int hashCode() {
int hash = LangUtils.HASH_SEED;
hash = LangUtils.hashCode(hash, this.name);
hash = LangUtils.hashCode(hash, this.value);
return hash;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

View file

@ -0,0 +1,83 @@
/*
* ====================================================================
* 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.message;
import java.io.Serializable;
import ch.boye.httpclientandroidlib.ProtocolVersion;
import ch.boye.httpclientandroidlib.RequestLine;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Basic implementation of {@link RequestLine}.
*
* @since 4.0
*/
@Immutable
public class BasicRequestLine implements RequestLine, Cloneable, Serializable {
private static final long serialVersionUID = 2810581718468737193L;
private final ProtocolVersion protoversion;
private final String method;
private final String uri;
public BasicRequestLine(final String method,
final String uri,
final ProtocolVersion version) {
super();
this.method = Args.notNull(method, "Method");
this.uri = Args.notNull(uri, "URI");
this.protoversion = Args.notNull(version, "Version");
}
public String getMethod() {
return this.method;
}
public ProtocolVersion getProtocolVersion() {
return this.protoversion;
}
public String getUri() {
return this.uri;
}
@Override
public String toString() {
// no need for non-default formatting in toString()
return BasicLineFormatter.INSTANCE.formatRequestLine(null, this).toString();
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

View file

@ -0,0 +1,100 @@
/*
* ====================================================================
* 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.message;
import java.io.Serializable;
import ch.boye.httpclientandroidlib.ProtocolVersion;
import ch.boye.httpclientandroidlib.StatusLine;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Basic implementation of {@link StatusLine}
*
* @since 4.0
*/
@Immutable
public class BasicStatusLine implements StatusLine, Cloneable, Serializable {
private static final long serialVersionUID = -2443303766890459269L;
// ----------------------------------------------------- Instance Variables
/** The protocol version. */
private final ProtocolVersion protoVersion;
/** The status code. */
private final int statusCode;
/** The reason phrase. */
private final String reasonPhrase;
// ----------------------------------------------------------- Constructors
/**
* Creates a new status line with the given version, status, and reason.
*
* @param version the protocol version of the response
* @param statusCode the status code of the response
* @param reasonPhrase the reason phrase to the status code, or
* <code>null</code>
*/
public BasicStatusLine(final ProtocolVersion version, final int statusCode,
final String reasonPhrase) {
super();
this.protoVersion = Args.notNull(version, "Version");
this.statusCode = Args.notNegative(statusCode, "Status code");
this.reasonPhrase = reasonPhrase;
}
// --------------------------------------------------------- Public Methods
public int getStatusCode() {
return this.statusCode;
}
public ProtocolVersion getProtocolVersion() {
return this.protoVersion;
}
public String getReasonPhrase() {
return this.reasonPhrase;
}
@Override
public String toString() {
// no need for non-default formatting in toString()
return BasicLineFormatter.INSTANCE.formatStatusLine(null, this).toString();
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

View file

@ -0,0 +1,414 @@
/*
* ====================================================================
* 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.message;
import java.util.NoSuchElementException;
import ch.boye.httpclientandroidlib.HeaderIterator;
import ch.boye.httpclientandroidlib.ParseException;
import ch.boye.httpclientandroidlib.TokenIterator;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Basic implementation of a {@link TokenIterator}.
* This implementation parses <tt>#token<tt> sequences as
* defined by RFC 2616, section 2.
* It extends that definition somewhat beyond US-ASCII.
*
* @since 4.0
*/
@NotThreadSafe
public class BasicTokenIterator implements TokenIterator {
/** The HTTP separator characters. Defined in RFC 2616, section 2.2. */
// the order of the characters here is adjusted to put the
// most likely candidates at the beginning of the collection
public final static String HTTP_SEPARATORS = " ,;=()<>@:\\\"/[]?{}\t";
/** The iterator from which to obtain the next header. */
protected final HeaderIterator headerIt;
/**
* The value of the current header.
* This is the header value that includes {@link #currentToken}.
* Undefined if the iteration is over.
*/
protected String currentHeader;
/**
* The token to be returned by the next call to {@link #nextToken()}.
* <code>null</code> if the iteration is over.
*/
protected String currentToken;
/**
* The position after {@link #currentToken} in {@link #currentHeader}.
* Undefined if the iteration is over.
*/
protected int searchPos;
/**
* Creates a new instance of {@link BasicTokenIterator}.
*
* @param headerIterator the iterator for the headers to tokenize
*/
public BasicTokenIterator(final HeaderIterator headerIterator) {
super();
this.headerIt = Args.notNull(headerIterator, "Header iterator");
this.searchPos = findNext(-1);
}
// non-javadoc, see interface TokenIterator
public boolean hasNext() {
return (this.currentToken != null);
}
/**
* Obtains the next token from this iteration.
*
* @return the next token in this iteration
*
* @throws NoSuchElementException if the iteration is already over
* @throws ParseException if an invalid header value is encountered
*/
public String nextToken()
throws NoSuchElementException, ParseException {
if (this.currentToken == null) {
throw new NoSuchElementException("Iteration already finished.");
}
final String result = this.currentToken;
// updates currentToken, may trigger ParseException:
this.searchPos = findNext(this.searchPos);
return result;
}
/**
* Returns the next token.
* Same as {@link #nextToken}, but with generic return type.
*
* @return the next token in this iteration
*
* @throws NoSuchElementException if there are no more tokens
* @throws ParseException if an invalid header value is encountered
*/
public final Object next()
throws NoSuchElementException, ParseException {
return nextToken();
}
/**
* Removing tokens is not supported.
*
* @throws UnsupportedOperationException always
*/
public final void remove()
throws UnsupportedOperationException {
throw new UnsupportedOperationException
("Removing tokens is not supported.");
}
/**
* Determines the next token.
* If found, the token is stored in {@link #currentToken}.
* The return value indicates the position after the token
* in {@link #currentHeader}. If necessary, the next header
* will be obtained from {@link #headerIt}.
* If not found, {@link #currentToken} is set to <code>null</code>.
*
* @param pos the position in the current header at which to
* start the search, -1 to search in the first header
*
* @return the position after the found token in the current header, or
* negative if there was no next token
*
* @throws ParseException if an invalid header value is encountered
*/
protected int findNext(final int pos) throws ParseException {
int from = pos;
if (from < 0) {
// called from the constructor, initialize the first header
if (!this.headerIt.hasNext()) {
return -1;
}
this.currentHeader = this.headerIt.nextHeader().getValue();
from = 0;
} else {
// called after a token, make sure there is a separator
from = findTokenSeparator(from);
}
final int start = findTokenStart(from);
if (start < 0) {
this.currentToken = null;
return -1; // nothing found
}
final int end = findTokenEnd(start);
this.currentToken = createToken(this.currentHeader, start, end);
return end;
}
/**
* Creates a new token to be returned.
* Called from {@link #findNext findNext} after the token is identified.
* The default implementation simply calls
* {@link java.lang.String#substring String.substring}.
* <br/>
* If header values are significantly longer than tokens, and some
* tokens are permanently referenced by the application, there can
* be problems with garbage collection. A substring will hold a
* reference to the full characters of the original string and
* therefore occupies more memory than might be expected.
* To avoid this, override this method and create a new string
* instead of a substring.
*
* @param value the full header value from which to create a token
* @param start the index of the first token character
* @param end the index after the last token character
*
* @return a string representing the token identified by the arguments
*/
protected String createToken(final String value, final int start, final int end) {
return value.substring(start, end);
}
/**
* Determines the starting position of the next token.
* This method will iterate over headers if necessary.
*
* @param pos the position in the current header at which to
* start the search
*
* @return the position of the token start in the current header,
* negative if no token start could be found
*/
protected int findTokenStart(final int pos) {
int from = Args.notNegative(pos, "Search position");
boolean found = false;
while (!found && (this.currentHeader != null)) {
final int to = this.currentHeader.length();
while (!found && (from < to)) {
final char ch = this.currentHeader.charAt(from);
if (isTokenSeparator(ch) || isWhitespace(ch)) {
// whitspace and token separators are skipped
from++;
} else if (isTokenChar(this.currentHeader.charAt(from))) {
// found the start of a token
found = true;
} else {
throw new ParseException
("Invalid character before token (pos " + from +
"): " + this.currentHeader);
}
}
if (!found) {
if (this.headerIt.hasNext()) {
this.currentHeader = this.headerIt.nextHeader().getValue();
from = 0;
} else {
this.currentHeader = null;
}
}
} // while headers
return found ? from : -1;
}
/**
* Determines the position of the next token separator.
* Because of multi-header joining rules, the end of a
* header value is a token separator. This method does
* therefore not need to iterate over headers.
*
* @param pos the position in the current header at which to
* start the search
*
* @return the position of a token separator in the current header,
* or at the end
*
* @throws ParseException
* if a new token is found before a token separator.
* RFC 2616, section 2.1 explicitly requires a comma between
* tokens for <tt>#</tt>.
*/
protected int findTokenSeparator(final int pos) {
int from = Args.notNegative(pos, "Search position");
boolean found = false;
final int to = this.currentHeader.length();
while (!found && (from < to)) {
final char ch = this.currentHeader.charAt(from);
if (isTokenSeparator(ch)) {
found = true;
} else if (isWhitespace(ch)) {
from++;
} else if (isTokenChar(ch)) {
throw new ParseException
("Tokens without separator (pos " + from +
"): " + this.currentHeader);
} else {
throw new ParseException
("Invalid character after token (pos " + from +
"): " + this.currentHeader);
}
}
return from;
}
/**
* Determines the ending position of the current token.
* This method will not leave the current header value,
* since the end of the header value is a token boundary.
*
* @param from the position of the first character of the token
*
* @return the position after the last character of the token.
* The behavior is undefined if <code>from</code> does not
* point to a token character in the current header value.
*/
protected int findTokenEnd(final int from) {
Args.notNegative(from, "Search position");
final int to = this.currentHeader.length();
int end = from+1;
while ((end < to) && isTokenChar(this.currentHeader.charAt(end))) {
end++;
}
return end;
}
/**
* Checks whether a character is a token separator.
* RFC 2616, section 2.1 defines comma as the separator for
* <tt>#token</tt> sequences. The end of a header value will
* also separate tokens, but that is not a character check.
*
* @param ch the character to check
*
* @return <code>true</code> if the character is a token separator,
* <code>false</code> otherwise
*/
protected boolean isTokenSeparator(final char ch) {
return (ch == ',');
}
/**
* Checks whether a character is a whitespace character.
* RFC 2616, section 2.2 defines space and horizontal tab as whitespace.
* The optional preceeding line break is irrelevant, since header
* continuation is handled transparently when parsing messages.
*
* @param ch the character to check
*
* @return <code>true</code> if the character is whitespace,
* <code>false</code> otherwise
*/
protected boolean isWhitespace(final char ch) {
// we do not use Character.isWhitspace(ch) here, since that allows
// many control characters which are not whitespace as per RFC 2616
return ((ch == '\t') || Character.isSpaceChar(ch));
}
/**
* Checks whether a character is a valid token character.
* Whitespace, control characters, and HTTP separators are not
* valid token characters. The HTTP specification (RFC 2616, section 2.2)
* defines tokens only for the US-ASCII character set, this
* method extends the definition to other character sets.
*
* @param ch the character to check
*
* @return <code>true</code> if the character is a valid token start,
* <code>false</code> otherwise
*/
protected boolean isTokenChar(final char ch) {
// common sense extension of ALPHA + DIGIT
if (Character.isLetterOrDigit(ch)) {
return true;
}
// common sense extension of CTL
if (Character.isISOControl(ch)) {
return false;
}
// no common sense extension for this
if (isHttpSeparator(ch)) {
return false;
}
// RFC 2616, section 2.2 defines a token character as
// "any CHAR except CTLs or separators". The controls
// and separators are included in the checks above.
// This will yield unexpected results for Unicode format characters.
// If that is a problem, overwrite isHttpSeparator(char) to filter
// out the false positives.
return true;
}
/**
* Checks whether a character is an HTTP separator.
* The implementation in this class checks only for the HTTP separators
* defined in RFC 2616, section 2.2. If you need to detect other
* separators beyond the US-ASCII character set, override this method.
*
* @param ch the character to check
*
* @return <code>true</code> if the character is an HTTP separator
*/
protected boolean isHttpSeparator(final char ch) {
return (HTTP_SEPARATORS.indexOf(ch) >= 0);
}
} // class BasicTokenIterator

View file

@ -0,0 +1,130 @@
/*
* ====================================================================
* 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.message;
import java.io.Serializable;
import ch.boye.httpclientandroidlib.FormattedHeader;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.ParseException;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* This class represents a raw HTTP header whose content is parsed 'on demand'
* only when the header value needs to be consumed.
*
* @since 4.0
*/
@NotThreadSafe
public class BufferedHeader implements FormattedHeader, Cloneable, Serializable {
private static final long serialVersionUID = -2768352615787625448L;
/**
* Header name.
*/
private final String name;
/**
* The buffer containing the entire header line.
*/
private final CharArrayBuffer buffer;
/**
* The beginning of the header value in the buffer
*/
private final int valuePos;
/**
* Creates a new header from a buffer.
* The name of the header will be parsed immediately,
* the value only if it is accessed.
*
* @param buffer the buffer containing the header to represent
*
* @throws ParseException in case of a parse error
*/
public BufferedHeader(final CharArrayBuffer buffer)
throws ParseException {
super();
Args.notNull(buffer, "Char array buffer");
final int colon = buffer.indexOf(':');
if (colon == -1) {
throw new ParseException
("Invalid header: " + buffer.toString());
}
final String s = buffer.substringTrimmed(0, colon);
if (s.length() == 0) {
throw new ParseException
("Invalid header: " + buffer.toString());
}
this.buffer = buffer;
this.name = s;
this.valuePos = colon + 1;
}
public String getName() {
return this.name;
}
public String getValue() {
return this.buffer.substringTrimmed(this.valuePos, this.buffer.length());
}
public HeaderElement[] getElements() throws ParseException {
final ParserCursor cursor = new ParserCursor(0, this.buffer.length());
cursor.updatePos(this.valuePos);
return BasicHeaderValueParser.INSTANCE.parseElements(this.buffer, cursor);
}
public int getValuePos() {
return this.valuePos;
}
public CharArrayBuffer getBuffer() {
return this.buffer;
}
@Override
public String toString() {
return this.buffer.toString();
}
@Override
public Object clone() throws CloneNotSupportedException {
// buffer is considered immutable
// no need to make a copy of it
return super.clone();
}
}

View file

@ -0,0 +1,311 @@
/*
* ====================================================================
* 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.message;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HeaderIterator;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* A class for combining a set of headers.
* This class allows for multiple headers with the same name and
* keeps track of the order in which headers were added.
*
*
* @since 4.0
*/
@NotThreadSafe
public class HeaderGroup implements Cloneable, Serializable {
private static final long serialVersionUID = 2608834160639271617L;
/** The list of headers for this group, in the order in which they were added */
private final List<Header> headers;
/**
* Constructor for HeaderGroup.
*/
public HeaderGroup() {
this.headers = new ArrayList<Header>(16);
}
/**
* Removes any contained headers.
*/
public void clear() {
headers.clear();
}
/**
* Adds the given header to the group. The order in which this header was
* added is preserved.
*
* @param header the header to add
*/
public void addHeader(final Header header) {
if (header == null) {
return;
}
headers.add(header);
}
/**
* Removes the given header.
*
* @param header the header to remove
*/
public void removeHeader(final Header header) {
if (header == null) {
return;
}
headers.remove(header);
}
/**
* Replaces the first occurence of the header with the same name. If no header with
* the same name is found the given header is added to the end of the list.
*
* @param header the new header that should replace the first header with the same
* name if present in the list.
*/
public void updateHeader(final Header header) {
if (header == null) {
return;
}
// HTTPCORE-361 : we don't use the for-each syntax, i.e.
// for (Header header : headers)
// as that creates an Iterator that needs to be garbage-collected
for (int i = 0; i < this.headers.size(); i++) {
final Header current = this.headers.get(i);
if (current.getName().equalsIgnoreCase(header.getName())) {
this.headers.set(i, header);
return;
}
}
this.headers.add(header);
}
/**
* Sets all of the headers contained within this group overriding any
* existing headers. The headers are added in the order in which they appear
* in the array.
*
* @param headers the headers to set
*/
public void setHeaders(final Header[] headers) {
clear();
if (headers == null) {
return;
}
Collections.addAll(this.headers, headers);
}
/**
* Gets a header representing all of the header values with the given name.
* If more that one header with the given name exists the values will be
* combined with a "," as per RFC 2616.
*
* <p>Header name comparison is case insensitive.
*
* @param name the name of the header(s) to get
* @return a header with a condensed value or <code>null</code> if no
* headers by the given name are present
*/
public Header getCondensedHeader(final String name) {
final Header[] hdrs = getHeaders(name);
if (hdrs.length == 0) {
return null;
} else if (hdrs.length == 1) {
return hdrs[0];
} else {
final CharArrayBuffer valueBuffer = new CharArrayBuffer(128);
valueBuffer.append(hdrs[0].getValue());
for (int i = 1; i < hdrs.length; i++) {
valueBuffer.append(", ");
valueBuffer.append(hdrs[i].getValue());
}
return new BasicHeader(name.toLowerCase(Locale.ENGLISH), valueBuffer.toString());
}
}
/**
* Gets all of the headers with the given name. The returned array
* maintains the relative order in which the headers were added.
*
* <p>Header name comparison is case insensitive.
*
* @param name the name of the header(s) to get
*
* @return an array of length >= 0
*/
public Header[] getHeaders(final String name) {
final List<Header> headersFound = new ArrayList<Header>();
// HTTPCORE-361 : we don't use the for-each syntax, i.e.
// for (Header header : headers)
// as that creates an Iterator that needs to be garbage-collected
for (int i = 0; i < this.headers.size(); i++) {
final Header header = this.headers.get(i);
if (header.getName().equalsIgnoreCase(name)) {
headersFound.add(header);
}
}
return headersFound.toArray(new Header[headersFound.size()]);
}
/**
* Gets the first header with the given name.
*
* <p>Header name comparison is case insensitive.
*
* @param name the name of the header to get
* @return the first header or <code>null</code>
*/
public Header getFirstHeader(final String name) {
// HTTPCORE-361 : we don't use the for-each syntax, i.e.
// for (Header header : headers)
// as that creates an Iterator that needs to be garbage-collected
for (int i = 0; i < this.headers.size(); i++) {
final Header header = this.headers.get(i);
if (header.getName().equalsIgnoreCase(name)) {
return header;
}
}
return null;
}
/**
* Gets the last header with the given name.
*
* <p>Header name comparison is case insensitive.
*
* @param name the name of the header to get
* @return the last header or <code>null</code>
*/
public Header getLastHeader(final String name) {
// start at the end of the list and work backwards
for (int i = headers.size() - 1; i >= 0; i--) {
final Header header = headers.get(i);
if (header.getName().equalsIgnoreCase(name)) {
return header;
}
}
return null;
}
/**
* Gets all of the headers contained within this group.
*
* @return an array of length >= 0
*/
public Header[] getAllHeaders() {
return headers.toArray(new Header[headers.size()]);
}
/**
* Tests if headers with the given name are contained within this group.
*
* <p>Header name comparison is case insensitive.
*
* @param name the header name to test for
* @return <code>true</code> if at least one header with the name is
* contained, <code>false</code> otherwise
*/
public boolean containsHeader(final String name) {
// HTTPCORE-361 : we don't use the for-each syntax, i.e.
// for (Header header : headers)
// as that creates an Iterator that needs to be garbage-collected
for (int i = 0; i < this.headers.size(); i++) {
final Header header = this.headers.get(i);
if (header.getName().equalsIgnoreCase(name)) {
return true;
}
}
return false;
}
/**
* Returns an iterator over this group of headers.
*
* @return iterator over this group of headers.
*
* @since 4.0
*/
public HeaderIterator iterator() {
return new BasicListHeaderIterator(this.headers, null);
}
/**
* Returns an iterator over the headers with a given name in this group.
*
* @param name the name of the headers over which to iterate, or
* <code>null</code> for all headers
*
* @return iterator over some headers in this group.
*
* @since 4.0
*/
public HeaderIterator iterator(final String name) {
return new BasicListHeaderIterator(this.headers, name);
}
/**
* Returns a copy of this object
*
* @return copy of this object
*
* @since 4.0
*/
public HeaderGroup copy() {
final HeaderGroup clone = new HeaderGroup();
clone.headers.addAll(this.headers);
return clone;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return this.headers.toString();
}
}

View file

@ -0,0 +1,122 @@
/*
* ====================================================================
* 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.message;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.NameValuePair;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* Interface for formatting elements of a header value.
* This is the complement to {@link HeaderValueParser}.
* Instances of this interface are expected to be stateless and thread-safe.
*
* <p>
* All formatting methods accept an optional buffer argument.
* If a buffer is passed in, the formatted element will be appended
* and the modified buffer is returned. If no buffer is passed in,
* a new buffer will be created and filled with the formatted element.
* In both cases, the caller is allowed to modify the returned buffer.
* </p>
*
* @since 4.0
*/
public interface HeaderValueFormatter {
/**
* Formats an array of header elements.
*
* @param buffer the buffer to append to, or
* <code>null</code> to create a new buffer
* @param elems the header elements to format
* @param quote <code>true</code> to always format with quoted values,
* <code>false</code> to use quotes only when necessary
*
* @return a buffer with the formatted header elements.
* If the <code>buffer</code> argument was not <code>null</code>,
* that buffer will be used and returned.
*/
CharArrayBuffer formatElements(CharArrayBuffer buffer,
HeaderElement[] elems,
boolean quote);
/**
* Formats one header element.
*
* @param buffer the buffer to append to, or
* <code>null</code> to create a new buffer
* @param elem the header element to format
* @param quote <code>true</code> to always format with quoted values,
* <code>false</code> to use quotes only when necessary
*
* @return a buffer with the formatted header element.
* If the <code>buffer</code> argument was not <code>null</code>,
* that buffer will be used and returned.
*/
CharArrayBuffer formatHeaderElement(CharArrayBuffer buffer,
HeaderElement elem,
boolean quote);
/**
* Formats the parameters of a header element.
* That's a list of name-value pairs, to be separated by semicolons.
* This method will <i>not</i> generate a leading semicolon.
*
* @param buffer the buffer to append to, or
* <code>null</code> to create a new buffer
* @param nvps the parameters (name-value pairs) to format
* @param quote <code>true</code> to always format with quoted values,
* <code>false</code> to use quotes only when necessary
*
* @return a buffer with the formatted parameters.
* If the <code>buffer</code> argument was not <code>null</code>,
* that buffer will be used and returned.
*/
CharArrayBuffer formatParameters(CharArrayBuffer buffer,
NameValuePair[] nvps,
boolean quote);
/**
* Formats one name-value pair, where the value is optional.
*
* @param buffer the buffer to append to, or
* <code>null</code> to create a new buffer
* @param nvp the name-value pair to format
* @param quote <code>true</code> to always format with a quoted value,
* <code>false</code> to use quotes only when necessary
*
* @return a buffer with the formatted name-value pair.
* If the <code>buffer</code> argument was not <code>null</code>,
* that buffer will be used and returned.
*/
CharArrayBuffer formatNameValuePair(CharArrayBuffer buffer,
NameValuePair nvp,
boolean quote);
}

View file

@ -0,0 +1,134 @@
/*
* ====================================================================
* 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.message;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.NameValuePair;
import ch.boye.httpclientandroidlib.ParseException;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* Interface for parsing header values into elements.
* Instances of this interface are expected to be stateless and thread-safe.
*
* @since 4.0
*/
public interface HeaderValueParser {
/**
* Parses a header value into elements.
* Parse errors are indicated as <code>RuntimeException</code>.
* <p>
* Some HTTP headers (such as the set-cookie header) have values that
* can be decomposed into multiple elements. In order to be processed
* by this parser, such headers must be in the following form:
* </p>
* <pre>
* header = [ element ] *( "," [ element ] )
* element = name [ "=" [ value ] ] *( ";" [ param ] )
* param = name [ "=" [ value ] ]
*
* name = token
* value = ( token | quoted-string )
*
* token = 1*&lt;any char except "=", ",", ";", &lt;"&gt; and
* white space&gt;
* quoted-string = &lt;"&gt; *( text | quoted-char ) &lt;"&gt;
* text = any char except &lt;"&gt;
* quoted-char = "\" char
* </pre>
* <p>
* Any amount of white space is allowed between any part of the
* header, element or param and is ignored. A missing value in any
* element or param will be stored as the empty {@link String};
* if the "=" is also missing <var>null</var> will be stored instead.
* </p>
*
* @param buffer buffer holding the header value to parse
* @param cursor the parser cursor containing the current position and
* the bounds within the buffer for the parsing operation
*
* @return an array holding all elements of the header value
*
* @throws ParseException in case of a parse error
*/
HeaderElement[] parseElements(
CharArrayBuffer buffer,
ParserCursor cursor) throws ParseException;
/**
* Parses a single header element.
* A header element consist of a semicolon-separate list
* of name=value definitions.
*
* @param buffer buffer holding the element to parse
* @param cursor the parser cursor containing the current position and
* the bounds within the buffer for the parsing operation
*
* @return the parsed element
*
* @throws ParseException in case of a parse error
*/
HeaderElement parseHeaderElement(
CharArrayBuffer buffer,
ParserCursor cursor) throws ParseException;
/**
* Parses a list of name-value pairs.
* These lists are used to specify parameters to a header element.
* Parse errors are indicated as <code>ParseException</code>.
*
* @param buffer buffer holding the name-value list to parse
* @param cursor the parser cursor containing the current position and
* the bounds within the buffer for the parsing operation
*
* @return an array holding all items of the name-value list
*
* @throws ParseException in case of a parse error
*/
NameValuePair[] parseParameters(
CharArrayBuffer buffer,
ParserCursor cursor) throws ParseException;
/**
* Parses a name=value specification, where the = and value are optional.
*
* @param buffer the buffer holding the name-value pair to parse
* @param cursor the parser cursor containing the current position and
* the bounds within the buffer for the parsing operation
*
* @return the name-value pair, where the value is <code>null</code>
* if no value is specified
*/
NameValuePair parseNameValuePair(
CharArrayBuffer buffer,
ParserCursor cursor) throws ParseException;
}

View file

@ -0,0 +1,131 @@
/*
* ====================================================================
* 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.message;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.ProtocolVersion;
import ch.boye.httpclientandroidlib.RequestLine;
import ch.boye.httpclientandroidlib.StatusLine;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* Interface for formatting elements of the HEAD section of an HTTP message.
* This is the complement to {@link LineParser}.
* There are individual methods for formatting a request line, a
* status line, or a header line. The formatting does <i>not</i> include the
* trailing line break sequence CR-LF.
* Instances of this interface are expected to be stateless and thread-safe.
*
* <p>
* The formatted lines are returned in memory, the formatter does not depend
* on any specific IO mechanism.
* In order to avoid unnecessary creation of temporary objects,
* a buffer can be passed as argument to all formatting methods.
* The implementation may or may not actually use that buffer for formatting.
* If it is used, the buffer will first be cleared by the
* <code>formatXXX</code> methods.
* The argument buffer can always be re-used after the call. The buffer
* returned as the result, if it is different from the argument buffer,
* MUST NOT be modified.
* </p>
*
* @since 4.0
*/
public interface LineFormatter {
/**
* Formats a protocol version.
* This method does <i>not</i> follow the general contract for
* <code>buffer</code> arguments.
* It does <i>not</i> clear the argument buffer, but appends instead.
* The returned buffer can always be modified by the caller.
* Because of these differing conventions, it is not named
* <code>formatProtocolVersion</code>.
*
* @param buffer a buffer to which to append, or <code>null</code>
* @param version the protocol version to format
*
* @return a buffer with the formatted protocol version appended.
* The caller is allowed to modify the result buffer.
* If the <code>buffer</code> argument is not <code>null</code>,
* the returned buffer is the argument buffer.
*/
CharArrayBuffer appendProtocolVersion(CharArrayBuffer buffer,
ProtocolVersion version);
/**
* Formats a request line.
*
* @param buffer a buffer available for formatting, or
* <code>null</code>.
* The buffer will be cleared before use.
* @param reqline the request line to format
*
* @return the formatted request line
*/
CharArrayBuffer formatRequestLine(CharArrayBuffer buffer,
RequestLine reqline);
/**
* Formats a status line.
*
* @param buffer a buffer available for formatting, or
* <code>null</code>.
* The buffer will be cleared before use.
* @param statline the status line to format
*
* @return the formatted status line
*
* @throws ch.boye.httpclientandroidlib.ParseException in case of a parse error
*/
CharArrayBuffer formatStatusLine(CharArrayBuffer buffer,
StatusLine statline);
/**
* Formats a header.
* Due to header continuation, the result may be multiple lines.
* In order to generate well-formed HTTP, the lines in the result
* must be separated by the HTTP line break sequence CR-LF.
* There is <i>no</i> trailing CR-LF in the result.
* <br/>
* See the class comment for details about the buffer argument.
*
* @param buffer a buffer available for formatting, or
* <code>null</code>.
* The buffer will be cleared before use.
* @param header the header to format
*
* @return a buffer holding the formatted header, never <code>null</code>.
* The returned buffer may be different from the argument buffer.
*
* @throws ch.boye.httpclientandroidlib.ParseException in case of a parse error
*/
CharArrayBuffer formatHeader(CharArrayBuffer buffer,
Header header);
}

View file

@ -0,0 +1,137 @@
/*
* ====================================================================
* 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.message;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.ParseException;
import ch.boye.httpclientandroidlib.ProtocolVersion;
import ch.boye.httpclientandroidlib.RequestLine;
import ch.boye.httpclientandroidlib.StatusLine;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* Interface for parsing lines in the HEAD section of an HTTP message.
* There are individual methods for parsing a request line, a
* status line, or a header line.
* The lines to parse are passed in memory, the parser does not depend
* on any specific IO mechanism.
* Instances of this interface are expected to be stateless and thread-safe.
*
* @since 4.0
*/
public interface LineParser {
/**
* Parses the textual representation of a protocol version.
* This is needed for parsing request lines (last element)
* as well as status lines (first element).
*
* @param buffer a buffer holding the protocol version to parse
* @param cursor the parser cursor containing the current position and
* the bounds within the buffer for the parsing operation
*
* @return the parsed protocol version
*
* @throws ParseException in case of a parse error
*/
ProtocolVersion parseProtocolVersion(
CharArrayBuffer buffer,
ParserCursor cursor) throws ParseException;
/**
* Checks whether there likely is a protocol version in a line.
* This method implements a <i>heuristic</i> to check for a
* likely protocol version specification. It does <i>not</i>
* guarantee that {@link #parseProtocolVersion} would not
* detect a parse error.
* This can be used to detect garbage lines before a request
* or status line.
*
* @param buffer a buffer holding the line to inspect
* @param cursor the cursor at which to check for a protocol version, or
* negative for "end of line". Whether the check tolerates
* whitespace before or after the protocol version is
* implementation dependent.
*
* @return <code>true</code> if there is a protocol version at the
* argument index (possibly ignoring whitespace),
* <code>false</code> otherwise
*/
boolean hasProtocolVersion(
CharArrayBuffer buffer,
ParserCursor cursor);
/**
* Parses a request line.
*
* @param buffer a buffer holding the line to parse
* @param cursor the parser cursor containing the current position and
* the bounds within the buffer for the parsing operation
*
* @return the parsed request line
*
* @throws ParseException in case of a parse error
*/
RequestLine parseRequestLine(
CharArrayBuffer buffer,
ParserCursor cursor) throws ParseException;
/**
* Parses a status line.
*
* @param buffer a buffer holding the line to parse
* @param cursor the parser cursor containing the current position and
* the bounds within the buffer for the parsing operation
*
* @return the parsed status line
*
* @throws ParseException in case of a parse error
*/
StatusLine parseStatusLine(
CharArrayBuffer buffer,
ParserCursor cursor) throws ParseException;
/**
* Creates a header from a line.
* The full header line is expected here. Header continuation lines
* must be joined by the caller before invoking this method.
*
* @param buffer a buffer holding the full header line.
* This buffer MUST NOT be re-used afterwards, since
* the returned object may reference the contents later.
*
* @return the header in the argument buffer.
* The returned object MAY be a wrapper for the argument buffer.
* The argument buffer MUST NOT be re-used or changed afterwards.
*
* @throws ParseException in case of a parse error
*/
Header parseHeader(CharArrayBuffer buffer)
throws ParseException;
}

View file

@ -0,0 +1,100 @@
/*
* ====================================================================
* 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.message;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
/**
* This class represents a context of a parsing operation:
* <ul>
* <li>the current position the parsing operation is expected to start at</li>
* <li>the bounds limiting the scope of the parsing operation</li>
* </ul>
*
* @since 4.0
*/
@NotThreadSafe
public class ParserCursor {
private final int lowerBound;
private final int upperBound;
private int pos;
public ParserCursor(final int lowerBound, final int upperBound) {
super();
if (lowerBound < 0) {
throw new IndexOutOfBoundsException("Lower bound cannot be negative");
}
if (lowerBound > upperBound) {
throw new IndexOutOfBoundsException("Lower bound cannot be greater then upper bound");
}
this.lowerBound = lowerBound;
this.upperBound = upperBound;
this.pos = lowerBound;
}
public int getLowerBound() {
return this.lowerBound;
}
public int getUpperBound() {
return this.upperBound;
}
public int getPos() {
return this.pos;
}
public void updatePos(final int pos) {
if (pos < this.lowerBound) {
throw new IndexOutOfBoundsException("pos: "+pos+" < lowerBound: "+this.lowerBound);
}
if (pos > this.upperBound) {
throw new IndexOutOfBoundsException("pos: "+pos+" > upperBound: "+this.upperBound);
}
this.pos = pos;
}
public boolean atEnd() {
return this.pos >= this.upperBound;
}
@Override
public String toString() {
final StringBuilder buffer = new StringBuilder();
buffer.append('[');
buffer.append(Integer.toString(this.lowerBound));
buffer.append('>');
buffer.append(Integer.toString(this.pos));
buffer.append('>');
buffer.append(Integer.toString(this.upperBound));
buffer.append(']');
return buffer.toString();
}
}

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/>.
*
*/
/**
* Core HTTP message components, message element parser
* and writer APIs and their default implementations.
*/
package ch.boye.httpclientandroidlib.message;