diff options
author | Narayan Kamath <narayan@google.com> | 2015-02-13 09:51:38 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-02-13 09:51:38 +0000 |
commit | 693d3a3639a854bc3d39bfb9f87550f5365e29b5 (patch) | |
tree | 409844a839ab4e3191253ea42d1833f270390ed8 /android/src/com | |
parent | adec7c30f9206305b615710be48d4649831d8b2c (diff) | |
parent | a8b46a3d3b6ed1488df10740653829283572903b (diff) | |
download | android_external_apache-http-693d3a3639a854bc3d39bfb9f87550f5365e29b5.tar.gz android_external_apache-http-693d3a3639a854bc3d39bfb9f87550f5365e29b5.tar.bz2 android_external_apache-http-693d3a3639a854bc3d39bfb9f87550f5365e29b5.zip |
Merge "Move apache specific portions of android.net.http to apache-http."
Diffstat (limited to 'android/src/com')
8 files changed, 1512 insertions, 0 deletions
diff --git a/android/src/com/android/internal/http/multipart/ByteArrayPartSource.java b/android/src/com/android/internal/http/multipart/ByteArrayPartSource.java new file mode 100644 index 0000000..faaac7f --- /dev/null +++ b/android/src/com/android/internal/http/multipart/ByteArrayPartSource.java @@ -0,0 +1,86 @@ +/* + * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/ByteArrayPartSource.java,v 1.7 2004/04/18 23:51:37 jsdever Exp $ + * $Revision: 480424 $ + * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $ + * + * ==================================================================== + * + * 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 com.android.internal.http.multipart; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +/** + * A PartSource that reads from a byte array. This class should be used when + * the data to post is already loaded into memory. + * + * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a> + * + * @since 2.0 + */ +public class ByteArrayPartSource implements PartSource { + + /** Name of the source file. */ + private String fileName; + + /** Byte array of the source file. */ + private byte[] bytes; + + /** + * Constructor for ByteArrayPartSource. + * + * @param fileName the name of the file these bytes represent + * @param bytes the content of this part + */ + public ByteArrayPartSource(String fileName, byte[] bytes) { + + this.fileName = fileName; + this.bytes = bytes; + + } + + /** + * @see PartSource#getLength() + */ + public long getLength() { + return bytes.length; + } + + /** + * @see PartSource#getFileName() + */ + public String getFileName() { + return fileName; + } + + /** + * @see PartSource#createInputStream() + */ + public InputStream createInputStream() { + return new ByteArrayInputStream(bytes); + } + +} diff --git a/android/src/com/android/internal/http/multipart/FilePart.java b/android/src/com/android/internal/http/multipart/FilePart.java new file mode 100644 index 0000000..bfcda00 --- /dev/null +++ b/android/src/com/android/internal/http/multipart/FilePart.java @@ -0,0 +1,254 @@ +/* + * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/FilePart.java,v 1.19 2004/04/18 23:51:37 jsdever Exp $ + * $Revision: 480424 $ + * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $ + * + * ==================================================================== + * + * 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 com.android.internal.http.multipart; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import org.apache.http.util.EncodingUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This class implements a part of a Multipart post object that + * consists of a file. + * + * @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a> + * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a> + * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a> + * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a> + * @author <a href="mailto:mdiggory@latte.harvard.edu">Mark Diggory</a> + * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a> + * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a> + * + * @since 2.0 + * + */ +public class FilePart extends PartBase { + + /** Default content encoding of file attachments. */ + public static final String DEFAULT_CONTENT_TYPE = "application/octet-stream"; + + /** Default charset of file attachments. */ + public static final String DEFAULT_CHARSET = "ISO-8859-1"; + + /** Default transfer encoding of file attachments. */ + public static final String DEFAULT_TRANSFER_ENCODING = "binary"; + + /** Log object for this class. */ + private static final Log LOG = LogFactory.getLog(FilePart.class); + + /** Attachment's file name */ + protected static final String FILE_NAME = "; filename="; + + /** Attachment's file name as a byte array */ + private static final byte[] FILE_NAME_BYTES = + EncodingUtils.getAsciiBytes(FILE_NAME); + + /** Source of the file part. */ + private PartSource source; + + /** + * FilePart Constructor. + * + * @param name the name for this part + * @param partSource the source for this part + * @param contentType the content type for this part, if <code>null</code> the + * {@link #DEFAULT_CONTENT_TYPE default} is used + * @param charset the charset encoding for this part, if <code>null</code> the + * {@link #DEFAULT_CHARSET default} is used + */ + public FilePart(String name, PartSource partSource, String contentType, String charset) { + + super( + name, + contentType == null ? DEFAULT_CONTENT_TYPE : contentType, + charset == null ? "ISO-8859-1" : charset, + DEFAULT_TRANSFER_ENCODING + ); + + if (partSource == null) { + throw new IllegalArgumentException("Source may not be null"); + } + this.source = partSource; + } + + /** + * FilePart Constructor. + * + * @param name the name for this part + * @param partSource the source for this part + */ + public FilePart(String name, PartSource partSource) { + this(name, partSource, null, null); + } + + /** + * FilePart Constructor. + * + * @param name the name of the file part + * @param file the file to post + * + * @throws FileNotFoundException if the <i>file</i> is not a normal + * file or if it is not readable. + */ + public FilePart(String name, File file) + throws FileNotFoundException { + this(name, new FilePartSource(file), null, null); + } + + /** + * FilePart Constructor. + * + * @param name the name of the file part + * @param file the file to post + * @param contentType the content type for this part, if <code>null</code> the + * {@link #DEFAULT_CONTENT_TYPE default} is used + * @param charset the charset encoding for this part, if <code>null</code> the + * {@link #DEFAULT_CHARSET default} is used + * + * @throws FileNotFoundException if the <i>file</i> is not a normal + * file or if it is not readable. + */ + public FilePart(String name, File file, String contentType, String charset) + throws FileNotFoundException { + this(name, new FilePartSource(file), contentType, charset); + } + + /** + * FilePart Constructor. + * + * @param name the name of the file part + * @param fileName the file name + * @param file the file to post + * + * @throws FileNotFoundException if the <i>file</i> is not a normal + * file or if it is not readable. + */ + public FilePart(String name, String fileName, File file) + throws FileNotFoundException { + this(name, new FilePartSource(fileName, file), null, null); + } + + /** + * FilePart Constructor. + * + * @param name the name of the file part + * @param fileName the file name + * @param file the file to post + * @param contentType the content type for this part, if <code>null</code> the + * {@link #DEFAULT_CONTENT_TYPE default} is used + * @param charset the charset encoding for this part, if <code>null</code> the + * {@link #DEFAULT_CHARSET default} is used + * + * @throws FileNotFoundException if the <i>file</i> is not a normal + * file or if it is not readable. + */ + public FilePart(String name, String fileName, File file, String contentType, String charset) + throws FileNotFoundException { + this(name, new FilePartSource(fileName, file), contentType, charset); + } + + /** + * Write the disposition header to the output stream + * @param out The output stream + * @throws IOException If an IO problem occurs + * @see Part#sendDispositionHeader(OutputStream) + */ + @Override + protected void sendDispositionHeader(OutputStream out) + throws IOException { + LOG.trace("enter sendDispositionHeader(OutputStream out)"); + super.sendDispositionHeader(out); + String filename = this.source.getFileName(); + if (filename != null) { + out.write(FILE_NAME_BYTES); + out.write(QUOTE_BYTES); + out.write(EncodingUtils.getAsciiBytes(filename)); + out.write(QUOTE_BYTES); + } + } + + /** + * Write the data in "source" to the specified stream. + * @param out The output stream. + * @throws IOException if an IO problem occurs. + * @see Part#sendData(OutputStream) + */ + @Override + protected void sendData(OutputStream out) throws IOException { + LOG.trace("enter sendData(OutputStream out)"); + if (lengthOfData() == 0) { + + // this file contains no data, so there is nothing to send. + // we don't want to create a zero length buffer as this will + // cause an infinite loop when reading. + LOG.debug("No data to send."); + return; + } + + byte[] tmp = new byte[4096]; + InputStream instream = source.createInputStream(); + try { + int len; + while ((len = instream.read(tmp)) >= 0) { + out.write(tmp, 0, len); + } + } finally { + // we're done with the stream, close it + instream.close(); + } + } + + /** + * Returns the source of the file part. + * + * @return The source. + */ + protected PartSource getSource() { + LOG.trace("enter getSource()"); + return this.source; + } + + /** + * Return the length of the data. + * @return The length. + * @see Part#lengthOfData() + */ + @Override + protected long lengthOfData() { + LOG.trace("enter lengthOfData()"); + return source.getLength(); + } + +} diff --git a/android/src/com/android/internal/http/multipart/FilePartSource.java b/android/src/com/android/internal/http/multipart/FilePartSource.java new file mode 100644 index 0000000..eb5cc0f --- /dev/null +++ b/android/src/com/android/internal/http/multipart/FilePartSource.java @@ -0,0 +1,131 @@ +/* + * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/FilePartSource.java,v 1.10 2004/04/18 23:51:37 jsdever Exp $ + * $Revision: 480424 $ + * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $ + * + * ==================================================================== + * + * 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 com.android.internal.http.multipart; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +/** + * A PartSource that reads from a File. + * + * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a> + * @author <a href="mailto:mdiggory@latte.harvard.edu">Mark Diggory</a> + * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a> + * + * @since 2.0 + */ +public class FilePartSource implements PartSource { + + /** File part file. */ + private File file = null; + + /** File part file name. */ + private String fileName = null; + + /** + * Constructor for FilePartSource. + * + * @param file the FilePart source File. + * + * @throws FileNotFoundException if the file does not exist or + * cannot be read + */ + public FilePartSource(File file) throws FileNotFoundException { + this.file = file; + if (file != null) { + if (!file.isFile()) { + throw new FileNotFoundException("File is not a normal file."); + } + if (!file.canRead()) { + throw new FileNotFoundException("File is not readable."); + } + this.fileName = file.getName(); + } + } + + /** + * Constructor for FilePartSource. + * + * @param fileName the file name of the FilePart + * @param file the source File for the FilePart + * + * @throws FileNotFoundException if the file does not exist or + * cannot be read + */ + public FilePartSource(String fileName, File file) + throws FileNotFoundException { + this(file); + if (fileName != null) { + this.fileName = fileName; + } + } + + /** + * Return the length of the file + * @return the length of the file. + * @see PartSource#getLength() + */ + public long getLength() { + if (this.file != null) { + return this.file.length(); + } else { + return 0; + } + } + + /** + * Return the current filename + * @return the filename. + * @see PartSource#getFileName() + */ + public String getFileName() { + return (fileName == null) ? "noname" : fileName; + } + + /** + * Return a new {@link FileInputStream} for the current filename. + * @return the new input stream. + * @throws IOException If an IO problem occurs. + * @see PartSource#createInputStream() + */ + public InputStream createInputStream() throws IOException { + if (this.file != null) { + return new FileInputStream(this.file); + } else { + return new ByteArrayInputStream(new byte[] {}); + } + } + +} diff --git a/android/src/com/android/internal/http/multipart/MultipartEntity.java b/android/src/com/android/internal/http/multipart/MultipartEntity.java new file mode 100644 index 0000000..2c5e7f6 --- /dev/null +++ b/android/src/com/android/internal/http/multipart/MultipartEntity.java @@ -0,0 +1,230 @@ +/* + * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/MultipartRequestEntity.java,v 1.1 2004/10/06 03:39:59 mbecke Exp $ + * $Revision: 502647 $ + * $Date: 2007-02-02 17:22:54 +0100 (Fri, 02 Feb 2007) $ + * + * ==================================================================== + * + * 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 com.android.internal.http.multipart; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Random; + +import org.apache.http.Header; +import org.apache.http.entity.AbstractHttpEntity; +import org.apache.http.message.BasicHeader; +import org.apache.http.params.HttpParams; +import org.apache.http.protocol.HTTP; +import org.apache.http.util.EncodingUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Implements a request entity suitable for an HTTP multipart POST method. + * <p> + * The HTTP multipart POST method is defined in section 3.3 of + * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC1867</a>: + * <blockquote> + * The media-type multipart/form-data follows the rules of all multipart + * MIME data streams as outlined in RFC 1521. The multipart/form-data contains + * a series of parts. Each part is expected to contain a content-disposition + * header where the value is "form-data" and a name attribute specifies + * the field name within the form, e.g., 'content-disposition: form-data; + * name="xxxxx"', where xxxxx is the field name corresponding to that field. + * Field names originally in non-ASCII character sets may be encoded using + * the method outlined in RFC 1522. + * </blockquote> + * </p> + * <p>This entity is designed to be used in conjunction with the + * {@link org.apache.http.HttpRequest} to provide + * multipart posts. Example usage:</p> + * <pre> + * File f = new File("/path/fileToUpload.txt"); + * HttpRequest request = new HttpRequest("http://host/some_path"); + * Part[] parts = { + * new StringPart("param_name", "value"), + * new FilePart(f.getName(), f) + * }; + * filePost.setEntity( + * new MultipartRequestEntity(parts, filePost.getParams()) + * ); + * HttpClient client = new HttpClient(); + * int status = client.executeMethod(filePost); + * </pre> + * + * @since 3.0 + */ +public class MultipartEntity extends AbstractHttpEntity { + + private static final Log log = LogFactory.getLog(MultipartEntity.class); + + /** The Content-Type for multipart/form-data. */ + private static final String MULTIPART_FORM_CONTENT_TYPE = "multipart/form-data"; + + /** + * Sets the value to use as the multipart boundary. + * <p> + * This parameter expects a value if type {@link String}. + * </p> + */ + public static final String MULTIPART_BOUNDARY = "http.method.multipart.boundary"; + + /** + * The pool of ASCII chars to be used for generating a multipart boundary. + */ + private static byte[] MULTIPART_CHARS = EncodingUtils.getAsciiBytes( + "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + + /** + * Generates a random multipart boundary string. + */ + private static byte[] generateMultipartBoundary() { + Random rand = new Random(); + byte[] bytes = new byte[rand.nextInt(11) + 30]; // a random size from 30 to 40 + for (int i = 0; i < bytes.length; i++) { + bytes[i] = MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]; + } + return bytes; + } + + /** The MIME parts as set by the constructor */ + protected Part[] parts; + + private byte[] multipartBoundary; + + private HttpParams params; + + private boolean contentConsumed = false; + + /** + * Creates a new multipart entity containing the given parts. + * @param parts The parts to include. + * @param params The params of the HttpMethod using this entity. + */ + public MultipartEntity(Part[] parts, HttpParams params) { + if (parts == null) { + throw new IllegalArgumentException("parts cannot be null"); + } + if (params == null) { + throw new IllegalArgumentException("params cannot be null"); + } + this.parts = parts; + this.params = params; + } + + public MultipartEntity(Part[] parts) { + setContentType(MULTIPART_FORM_CONTENT_TYPE); + if (parts == null) { + throw new IllegalArgumentException("parts cannot be null"); + } + this.parts = parts; + this.params = null; + } + + /** + * Returns the MIME boundary string that is used to demarcate boundaries of + * this part. The first call to this method will implicitly create a new + * boundary string. To create a boundary string first the + * HttpMethodParams.MULTIPART_BOUNDARY parameter is considered. Otherwise + * a random one is generated. + * + * @return The boundary string of this entity in ASCII encoding. + */ + protected byte[] getMultipartBoundary() { + if (multipartBoundary == null) { + String temp = null; + if (params != null) { + temp = (String) params.getParameter(MULTIPART_BOUNDARY); + } + if (temp != null) { + multipartBoundary = EncodingUtils.getAsciiBytes(temp); + } else { + multipartBoundary = generateMultipartBoundary(); + } + } + return multipartBoundary; + } + + /** + * Returns <code>true</code> if all parts are repeatable, <code>false</code> otherwise. + */ + public boolean isRepeatable() { + for (int i = 0; i < parts.length; i++) { + if (!parts[i].isRepeatable()) { + return false; + } + } + return true; + } + + /* (non-Javadoc) + */ + public void writeTo(OutputStream out) throws IOException { + Part.sendParts(out, parts, getMultipartBoundary()); + } + /* (non-Javadoc) + * @see org.apache.commons.http.AbstractHttpEntity.#getContentType() + */ + @Override + public Header getContentType() { + StringBuffer buffer = new StringBuffer(MULTIPART_FORM_CONTENT_TYPE); + buffer.append("; boundary="); + buffer.append(EncodingUtils.getAsciiString(getMultipartBoundary())); + return new BasicHeader(HTTP.CONTENT_TYPE, buffer.toString()); + + } + + /* (non-Javadoc) + */ + public long getContentLength() { + try { + return Part.getLengthOfParts(parts, getMultipartBoundary()); + } catch (Exception e) { + log.error("An exception occurred while getting the length of the parts", e); + return 0; + } + } + + public InputStream getContent() throws IOException, IllegalStateException { + if(!isRepeatable() && this.contentConsumed ) { + throw new IllegalStateException("Content has been consumed"); + } + this.contentConsumed = true; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Part.sendParts(baos, this.parts, this.multipartBoundary); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + return bais; + } + + public boolean isStreaming() { + return false; + } +} diff --git a/android/src/com/android/internal/http/multipart/Part.java b/android/src/com/android/internal/http/multipart/Part.java new file mode 100644 index 0000000..cb1b546 --- /dev/null +++ b/android/src/com/android/internal/http/multipart/Part.java @@ -0,0 +1,439 @@ +/* + * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/Part.java,v 1.16 2005/01/14 21:16:40 olegk Exp $ + * $Revision: 480424 $ + * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $ + * + * ==================================================================== + * + * 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 com.android.internal.http.multipart; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.http.util.EncodingUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Abstract class for one Part of a multipart post object. + * + * @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a> + * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a> + * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a> + * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a> + * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a> + * + * @since 2.0 + */ +public abstract class Part { + + /** Log object for this class. */ + private static final Log LOG = LogFactory.getLog(Part.class); + + /** + * The boundary + * @deprecated use {@link org.apache.http.client.methods.multipart#MULTIPART_BOUNDARY} + */ + protected static final String BOUNDARY = "----------------314159265358979323846"; + + /** + * The boundary as a byte array. + * @deprecated + */ + protected static final byte[] BOUNDARY_BYTES = EncodingUtils.getAsciiBytes(BOUNDARY); + + /** + * The default boundary to be used if {@link #setPartBoundary(byte[])} has not + * been called. + */ + private static final byte[] DEFAULT_BOUNDARY_BYTES = BOUNDARY_BYTES; + + /** Carriage return/linefeed */ + protected static final String CRLF = "\r\n"; + + /** Carriage return/linefeed as a byte array */ + protected static final byte[] CRLF_BYTES = EncodingUtils.getAsciiBytes(CRLF); + + /** Content dispostion characters */ + protected static final String QUOTE = "\""; + + /** Content dispostion as a byte array */ + protected static final byte[] QUOTE_BYTES = + EncodingUtils.getAsciiBytes(QUOTE); + + /** Extra characters */ + protected static final String EXTRA = "--"; + + /** Extra characters as a byte array */ + protected static final byte[] EXTRA_BYTES = + EncodingUtils.getAsciiBytes(EXTRA); + + /** Content dispostion characters */ + protected static final String CONTENT_DISPOSITION = "Content-Disposition: form-data; name="; + + /** Content dispostion as a byte array */ + protected static final byte[] CONTENT_DISPOSITION_BYTES = + EncodingUtils.getAsciiBytes(CONTENT_DISPOSITION); + + /** Content type header */ + protected static final String CONTENT_TYPE = "Content-Type: "; + + /** Content type header as a byte array */ + protected static final byte[] CONTENT_TYPE_BYTES = + EncodingUtils.getAsciiBytes(CONTENT_TYPE); + + /** Content charset */ + protected static final String CHARSET = "; charset="; + + /** Content charset as a byte array */ + protected static final byte[] CHARSET_BYTES = + EncodingUtils.getAsciiBytes(CHARSET); + + /** Content type header */ + protected static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding: "; + + /** Content type header as a byte array */ + protected static final byte[] CONTENT_TRANSFER_ENCODING_BYTES = + EncodingUtils.getAsciiBytes(CONTENT_TRANSFER_ENCODING); + + /** + * Return the boundary string. + * @return the boundary string + * @deprecated uses a constant string. Rather use {@link #getPartBoundary} + */ + public static String getBoundary() { + return BOUNDARY; + } + + /** + * The ASCII bytes to use as the multipart boundary. + */ + private byte[] boundaryBytes; + + /** + * Return the name of this part. + * @return The name. + */ + public abstract String getName(); + + /** + * Returns the content type of this part. + * @return the content type, or <code>null</code> to exclude the content type header + */ + public abstract String getContentType(); + + /** + * Return the character encoding of this part. + * @return the character encoding, or <code>null</code> to exclude the character + * encoding header + */ + public abstract String getCharSet(); + + /** + * Return the transfer encoding of this part. + * @return the transfer encoding, or <code>null</code> to exclude the transfer encoding header + */ + public abstract String getTransferEncoding(); + + /** + * Gets the part boundary to be used. + * @return the part boundary as an array of bytes. + * + * @since 3.0 + */ + protected byte[] getPartBoundary() { + if (boundaryBytes == null) { + // custom boundary bytes have not been set, use the default. + return DEFAULT_BOUNDARY_BYTES; + } else { + return boundaryBytes; + } + } + + /** + * Sets the part boundary. Only meant to be used by + * {@link Part#sendParts(OutputStream, Part[], byte[])} + * and {@link Part#getLengthOfParts(Part[], byte[])} + * @param boundaryBytes An array of ASCII bytes. + * @since 3.0 + */ + void setPartBoundary(byte[] boundaryBytes) { + this.boundaryBytes = boundaryBytes; + } + + /** + * Tests if this part can be sent more than once. + * @return <code>true</code> if {@link #sendData(OutputStream)} can be successfully called + * more than once. + * @since 3.0 + */ + public boolean isRepeatable() { + return true; + } + + /** + * Write the start to the specified output stream + * @param out The output stream + * @throws IOException If an IO problem occurs. + */ + protected void sendStart(OutputStream out) throws IOException { + LOG.trace("enter sendStart(OutputStream out)"); + out.write(EXTRA_BYTES); + out.write(getPartBoundary()); + out.write(CRLF_BYTES); + } + + /** + * Write the content disposition header to the specified output stream + * + * @param out The output stream + * @throws IOException If an IO problem occurs. + */ + protected void sendDispositionHeader(OutputStream out) throws IOException { + LOG.trace("enter sendDispositionHeader(OutputStream out)"); + out.write(CONTENT_DISPOSITION_BYTES); + out.write(QUOTE_BYTES); + out.write(EncodingUtils.getAsciiBytes(getName())); + out.write(QUOTE_BYTES); + } + + /** + * Write the content type header to the specified output stream + * @param out The output stream + * @throws IOException If an IO problem occurs. + */ + protected void sendContentTypeHeader(OutputStream out) throws IOException { + LOG.trace("enter sendContentTypeHeader(OutputStream out)"); + String contentType = getContentType(); + if (contentType != null) { + out.write(CRLF_BYTES); + out.write(CONTENT_TYPE_BYTES); + out.write(EncodingUtils.getAsciiBytes(contentType)); + String charSet = getCharSet(); + if (charSet != null) { + out.write(CHARSET_BYTES); + out.write(EncodingUtils.getAsciiBytes(charSet)); + } + } + } + + /** + * Write the content transfer encoding header to the specified + * output stream + * + * @param out The output stream + * @throws IOException If an IO problem occurs. + */ + protected void sendTransferEncodingHeader(OutputStream out) throws IOException { + LOG.trace("enter sendTransferEncodingHeader(OutputStream out)"); + String transferEncoding = getTransferEncoding(); + if (transferEncoding != null) { + out.write(CRLF_BYTES); + out.write(CONTENT_TRANSFER_ENCODING_BYTES); + out.write(EncodingUtils.getAsciiBytes(transferEncoding)); + } + } + + /** + * Write the end of the header to the output stream + * @param out The output stream + * @throws IOException If an IO problem occurs. + */ + protected void sendEndOfHeader(OutputStream out) throws IOException { + LOG.trace("enter sendEndOfHeader(OutputStream out)"); + out.write(CRLF_BYTES); + out.write(CRLF_BYTES); + } + + /** + * Write the data to the specified output stream + * @param out The output stream + * @throws IOException If an IO problem occurs. + */ + protected abstract void sendData(OutputStream out) throws IOException; + + /** + * Return the length of the main content + * + * @return long The length. + * @throws IOException If an IO problem occurs + */ + protected abstract long lengthOfData() throws IOException; + + /** + * Write the end data to the output stream. + * @param out The output stream + * @throws IOException If an IO problem occurs. + */ + protected void sendEnd(OutputStream out) throws IOException { + LOG.trace("enter sendEnd(OutputStream out)"); + out.write(CRLF_BYTES); + } + + /** + * Write all the data to the output stream. + * If you override this method make sure to override + * #length() as well + * + * @param out The output stream + * @throws IOException If an IO problem occurs. + */ + public void send(OutputStream out) throws IOException { + LOG.trace("enter send(OutputStream out)"); + sendStart(out); + sendDispositionHeader(out); + sendContentTypeHeader(out); + sendTransferEncodingHeader(out); + sendEndOfHeader(out); + sendData(out); + sendEnd(out); + } + + + /** + * Return the full length of all the data. + * If you override this method make sure to override + * #send(OutputStream) as well + * + * @return long The length. + * @throws IOException If an IO problem occurs + */ + public long length() throws IOException { + LOG.trace("enter length()"); + if (lengthOfData() < 0) { + return -1; + } + ByteArrayOutputStream overhead = new ByteArrayOutputStream(); + sendStart(overhead); + sendDispositionHeader(overhead); + sendContentTypeHeader(overhead); + sendTransferEncodingHeader(overhead); + sendEndOfHeader(overhead); + sendEnd(overhead); + return overhead.size() + lengthOfData(); + } + + /** + * Return a string representation of this object. + * @return A string representation of this object. + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return this.getName(); + } + + /** + * Write all parts and the last boundary to the specified output stream. + * + * @param out The stream to write to. + * @param parts The parts to write. + * + * @throws IOException If an I/O error occurs while writing the parts. + */ + public static void sendParts(OutputStream out, final Part[] parts) + throws IOException { + sendParts(out, parts, DEFAULT_BOUNDARY_BYTES); + } + + /** + * Write all parts and the last boundary to the specified output stream. + * + * @param out The stream to write to. + * @param parts The parts to write. + * @param partBoundary The ASCII bytes to use as the part boundary. + * + * @throws IOException If an I/O error occurs while writing the parts. + * + * @since 3.0 + */ + public static void sendParts(OutputStream out, Part[] parts, byte[] partBoundary) + throws IOException { + + if (parts == null) { + throw new IllegalArgumentException("Parts may not be null"); + } + if (partBoundary == null || partBoundary.length == 0) { + throw new IllegalArgumentException("partBoundary may not be empty"); + } + for (int i = 0; i < parts.length; i++) { + // set the part boundary before the part is sent + parts[i].setPartBoundary(partBoundary); + parts[i].send(out); + } + out.write(EXTRA_BYTES); + out.write(partBoundary); + out.write(EXTRA_BYTES); + out.write(CRLF_BYTES); + } + + /** + * Return the total sum of all parts and that of the last boundary + * + * @param parts The parts. + * @return The total length + * + * @throws IOException If an I/O error occurs while writing the parts. + */ + public static long getLengthOfParts(Part[] parts) + throws IOException { + return getLengthOfParts(parts, DEFAULT_BOUNDARY_BYTES); + } + + /** + * Gets the length of the multipart message including the given parts. + * + * @param parts The parts. + * @param partBoundary The ASCII bytes to use as the part boundary. + * @return The total length + * + * @throws IOException If an I/O error occurs while writing the parts. + * + * @since 3.0 + */ + public static long getLengthOfParts(Part[] parts, byte[] partBoundary) throws IOException { + LOG.trace("getLengthOfParts(Parts[])"); + if (parts == null) { + throw new IllegalArgumentException("Parts may not be null"); + } + long total = 0; + for (int i = 0; i < parts.length; i++) { + // set the part boundary before we calculate the part's length + parts[i].setPartBoundary(partBoundary); + long l = parts[i].length(); + if (l < 0) { + return -1; + } + total += l; + } + total += EXTRA_BYTES.length; + total += partBoundary.length; + total += EXTRA_BYTES.length; + total += CRLF_BYTES.length; + return total; + } +} diff --git a/android/src/com/android/internal/http/multipart/PartBase.java b/android/src/com/android/internal/http/multipart/PartBase.java new file mode 100644 index 0000000..876d15d --- /dev/null +++ b/android/src/com/android/internal/http/multipart/PartBase.java @@ -0,0 +1,150 @@ +/* + * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/PartBase.java,v 1.5 2004/04/18 23:51:37 jsdever Exp $ + * $Revision: 480424 $ + * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $ + * + * ==================================================================== + * + * 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 com.android.internal.http.multipart; + + +/** + * Provides setters and getters for the basic Part properties. + * + * @author Michael Becke + */ +public abstract class PartBase extends Part { + + /** Name of the file part. */ + private String name; + + /** Content type of the file part. */ + private String contentType; + + /** Content encoding of the file part. */ + private String charSet; + + /** The transfer encoding. */ + private String transferEncoding; + + /** + * Constructor. + * + * @param name The name of the part + * @param contentType The content type, or <code>null</code> + * @param charSet The character encoding, or <code>null</code> + * @param transferEncoding The transfer encoding, or <code>null</code> + */ + public PartBase(String name, String contentType, String charSet, String transferEncoding) { + + if (name == null) { + throw new IllegalArgumentException("Name must not be null"); + } + this.name = name; + this.contentType = contentType; + this.charSet = charSet; + this.transferEncoding = transferEncoding; + } + + /** + * Returns the name. + * @return The name. + * @see Part#getName() + */ + @Override + public String getName() { + return this.name; + } + + /** + * Returns the content type of this part. + * @return String The name. + */ + @Override + public String getContentType() { + return this.contentType; + } + + /** + * Return the character encoding of this part. + * @return String The name. + */ + @Override + public String getCharSet() { + return this.charSet; + } + + /** + * Returns the transfer encoding of this part. + * @return String The name. + */ + @Override + public String getTransferEncoding() { + return transferEncoding; + } + + /** + * Sets the character encoding. + * + * @param charSet the character encoding, or <code>null</code> to exclude the character + * encoding header + */ + public void setCharSet(String charSet) { + this.charSet = charSet; + } + + /** + * Sets the content type. + * + * @param contentType the content type, or <code>null</code> to exclude the content type header + */ + public void setContentType(String contentType) { + this.contentType = contentType; + } + + /** + * Sets the part name. + * + * @param name + */ + public void setName(String name) { + if (name == null) { + throw new IllegalArgumentException("Name must not be null"); + } + this.name = name; + } + + /** + * Sets the transfer encoding. + * + * @param transferEncoding the transfer encoding, or <code>null</code> to exclude the + * transfer encoding header + */ + public void setTransferEncoding(String transferEncoding) { + this.transferEncoding = transferEncoding; + } + +} diff --git a/android/src/com/android/internal/http/multipart/PartSource.java b/android/src/com/android/internal/http/multipart/PartSource.java new file mode 100644 index 0000000..3740696 --- /dev/null +++ b/android/src/com/android/internal/http/multipart/PartSource.java @@ -0,0 +1,72 @@ +/* + * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/PartSource.java,v 1.6 2004/04/18 23:51:37 jsdever Exp $ + * $Revision: 480424 $ + * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $ + * + * ==================================================================== + * + * 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 com.android.internal.http.multipart; + +import java.io.IOException; +import java.io.InputStream; + +/** + * An interface for providing access to data when posting MultiPart messages. + * + * @see FilePart + * + * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a> + * + * @since 2.0 + */ +public interface PartSource { + + /** + * Gets the number of bytes contained in this source. + * + * @return a value >= 0 + */ + long getLength(); + + /** + * Gets the name of the file this source represents. + * + * @return the fileName used for posting a MultiPart file part + */ + String getFileName(); + + /** + * Gets a new InputStream for reading this source. This method can be + * called more than once and should therefore return a new stream every + * time. + * + * @return a new InputStream + * + * @throws IOException if an error occurs when creating the InputStream + */ + InputStream createInputStream() throws IOException; + +} diff --git a/android/src/com/android/internal/http/multipart/StringPart.java b/android/src/com/android/internal/http/multipart/StringPart.java new file mode 100644 index 0000000..c98257e --- /dev/null +++ b/android/src/com/android/internal/http/multipart/StringPart.java @@ -0,0 +1,150 @@ +/* + * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/StringPart.java,v 1.11 2004/04/18 23:51:37 jsdever Exp $ + * $Revision: 480424 $ + * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $ + * + * ==================================================================== + * + * 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 com.android.internal.http.multipart; + +import java.io.OutputStream; +import java.io.IOException; + +import org.apache.http.util.EncodingUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Simple string parameter for a multipart post + * + * @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a> + * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a> + * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a> + * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a> + * + * @since 2.0 + */ +public class StringPart extends PartBase { + + /** Log object for this class. */ + private static final Log LOG = LogFactory.getLog(StringPart.class); + + /** Default content encoding of string parameters. */ + public static final String DEFAULT_CONTENT_TYPE = "text/plain"; + + /** Default charset of string parameters*/ + public static final String DEFAULT_CHARSET = "US-ASCII"; + + /** Default transfer encoding of string parameters*/ + public static final String DEFAULT_TRANSFER_ENCODING = "8bit"; + + /** Contents of this StringPart. */ + private byte[] content; + + /** The String value of this part. */ + private String value; + + /** + * Constructor. + * + * @param name The name of the part + * @param value the string to post + * @param charset the charset to be used to encode the string, if <code>null</code> + * the {@link #DEFAULT_CHARSET default} is used + */ + public StringPart(String name, String value, String charset) { + + super( + name, + DEFAULT_CONTENT_TYPE, + charset == null ? DEFAULT_CHARSET : charset, + DEFAULT_TRANSFER_ENCODING + ); + if (value == null) { + throw new IllegalArgumentException("Value may not be null"); + } + if (value.indexOf(0) != -1) { + // See RFC 2048, 2.8. "8bit Data" + throw new IllegalArgumentException("NULs may not be present in string parts"); + } + this.value = value; + } + + /** + * Constructor. + * + * @param name The name of the part + * @param value the string to post + */ + public StringPart(String name, String value) { + this(name, value, null); + } + + /** + * Gets the content in bytes. Bytes are lazily created to allow the charset to be changed + * after the part is created. + * + * @return the content in bytes + */ + private byte[] getContent() { + if (content == null) { + content = EncodingUtils.getBytes(value, getCharSet()); + } + return content; + } + + /** + * Writes the data to the given OutputStream. + * @param out the OutputStream to write to + * @throws IOException if there is a write error + */ + @Override + protected void sendData(OutputStream out) throws IOException { + LOG.trace("enter sendData(OutputStream)"); + out.write(getContent()); + } + + /** + * Return the length of the data. + * @return The length of the data. + * @see Part#lengthOfData() + */ + @Override + protected long lengthOfData() { + LOG.trace("enter lengthOfData()"); + return getContent().length; + } + + /* (non-Javadoc) + * @see org.apache.commons.httpclient.methods.multipart.BasePart#setCharSet(java.lang.String) + */ + @Override + public void setCharSet(String charSet) { + super.setCharSet(charSet); + this.content = null; + } + +} |