HTTPCLIENT-784: Improved browser compatibility mode of the HttpMultipart class; upgraded mime4j dependency to version 0.4-SNAPSHOT

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@675712 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2008-07-10 20:16:33 +00:00
parent ec0c961cbb
commit b5bac2833f
12 changed files with 131 additions and 182 deletions

View File

@ -69,24 +69,11 @@
<artifactId>apache-mime4j</artifactId>
<version>${mime4j.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>${commons-logging.version}</version>
</dependency>
<dependency>
<!-- To be removed as soon as apache-mime4j is upgraded to version 0.4
or GUMP has been made capable of handling of transitive dependencies
-->
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>

View File

@ -1,46 +0,0 @@
/*
* $HeadURL$
* $Revision$
* $Date$
*
* ====================================================================
* 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 org.apache.http.entity.mime;
import java.nio.charset.Charset;
public interface ContentDescriptor {
String getTransferEncoding();
String getMimeType();
Charset getCharset();
long getContentLength();
}

View File

@ -32,6 +32,7 @@
package org.apache.http.entity.mime;
import org.apache.http.entity.mime.content.ContentBody;
import org.apache.james.mime4j.ContentDescriptor;
import org.apache.james.mime4j.field.Field;
import org.apache.james.mime4j.message.BodyPart;
import org.apache.james.mime4j.message.Header;
@ -57,7 +58,7 @@ public class FormBodyPart extends BodyPart {
}
this.name = name;
Header header = new RFC822Header();
Header header = new Header();
setHeader(header);
setBody(body);
@ -92,7 +93,7 @@ public class FormBodyPart extends BodyPart {
buffer.append(desc.getMimeType());
if (desc.getCharset() != null) {
buffer.append("; charset=");
buffer.append(desc.getCharset().name());
buffer.append(desc.getCharset());
}
getHeader().addField(Field.parse(buffer.toString()));
}

View File

@ -32,15 +32,16 @@
package org.apache.http.entity.mime;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.util.List;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.http.entity.mime.content.ContentBody;
import org.apache.http.protocol.HTTP;
import org.apache.james.mime4j.MimeException;
import org.apache.james.mime4j.field.ContentTypeField;
import org.apache.james.mime4j.field.Field;
import org.apache.james.mime4j.message.Body;
@ -48,6 +49,7 @@ import org.apache.james.mime4j.message.BodyPart;
import org.apache.james.mime4j.message.Entity;
import org.apache.james.mime4j.message.Multipart;
import org.apache.james.mime4j.util.CharsetUtil;
import org.apache.james.mime4j.util.MessageUtils;
/**
* An extension of the mime4j standard {@link Multipart} class, which is
@ -60,8 +62,8 @@ public class HttpMultipart extends Multipart {
private HttpMultipartMode mode;
public HttpMultipart() {
super();
public HttpMultipart(final String subType) {
super(subType);
this.mode = HttpMultipartMode.STRICT;
}
@ -101,7 +103,10 @@ public class HttpMultipart extends Multipart {
return cField.getBoundary();
}
private void writeTo(final OutputStream out, boolean writeContent) throws IOException {
private void doWriteTo(
final HttpMultipartMode mode,
final OutputStream out,
boolean writeContent) throws IOException {
List<?> bodyParts = getBodyParts();
Charset charset = getCharset();
@ -111,10 +116,13 @@ public class HttpMultipart extends Multipart {
new OutputStreamWriter(out, charset),
8192);
switch (this.mode) {
switch (mode) {
case STRICT:
writer.write(getPreamble());
writer.write("\r\n");
String preamble = getPreamble();
if (preamble != null && preamble.length() != 0) {
writer.write(preamble);
writer.write("\r\n");
}
for (int i = 0; i < bodyParts.size(); i++) {
writer.write("--");
@ -122,9 +130,9 @@ public class HttpMultipart extends Multipart {
writer.write("\r\n");
writer.flush();
BodyPart part = (BodyPart) bodyParts.get(i);
part.getHeader().writeTo(out);
part.getHeader().writeTo(out, MessageUtils.STRICT_IGNORE);
if (writeContent) {
part.getBody().writeTo(out);
part.getBody().writeTo(out, MessageUtils.STRICT_IGNORE);
}
writer.write("\r\n");
}
@ -132,8 +140,11 @@ public class HttpMultipart extends Multipart {
writer.write("--");
writer.write(boundary);
writer.write("--\r\n");
writer.write(getEpilogue());
writer.write("\r\n");
String epilogue = getEpilogue();
if (epilogue != null && epilogue.length() != 0) {
writer.write(epilogue);
writer.write("\r\n");
}
writer.flush();
break;
case BROWSER_COMPATIBLE:
@ -142,8 +153,6 @@ public class HttpMultipart extends Multipart {
// (2) Only write Content-Disposition
// (3) Use content charset
writer.write("\r\n");
for (int i = 0; i < bodyParts.size(); i++) {
writer.write("--");
writer.write(boundary);
@ -157,7 +166,7 @@ public class HttpMultipart extends Multipart {
writer.write("\r\n");
writer.flush();
if (writeContent) {
part.getBody().writeTo(out);
part.getBody().writeTo(out, MessageUtils.LENIENT);
}
writer.write("\r\n");
@ -166,7 +175,6 @@ public class HttpMultipart extends Multipart {
writer.write("--");
writer.write(boundary);
writer.write("--\r\n");
writer.write("\r\n");
writer.flush();
break;
}
@ -179,9 +187,17 @@ public class HttpMultipart extends Multipart {
*
* @see #getMode()
*/
@Override
public void writeTo(final OutputStream out) throws IOException {
writeTo(out, true);
doWriteTo(this.mode, out, true);
}
@Override
public void writeTo(final OutputStream out, int mode) throws IOException, MimeException {
if (mode == MessageUtils.LENIENT) {
doWriteTo(HttpMultipartMode.BROWSER_COMPATIBLE, out, true);
} else {
doWriteTo(HttpMultipartMode.STRICT, out, true);
}
}
/**
@ -218,7 +234,7 @@ public class HttpMultipart extends Multipart {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
writeTo(out, false);
doWriteTo(this.mode, out, false);
byte[] extra = out.toByteArray();
return contentLen + extra.length;
} catch (IOException ex) {

View File

@ -73,14 +73,15 @@ public class MultipartEntity implements HttpEntity {
final String boundary,
final Charset charset) {
super();
this.multipart = new HttpMultipart();
this.multipart = new HttpMultipart("form-data");
this.contentType = new BasicHeader(
HTTP.CONTENT_TYPE,
generateContentType(boundary, charset));
this.dirty = true;
Message message = new Message();
org.apache.james.mime4j.message.Header header = new RFC822Header();
org.apache.james.mime4j.message.Header header =
new org.apache.james.mime4j.message.Header();
header.addField(
Field.parse("Content-Type: " + this.contentType.getValue()));
message.setHeader(header);

View File

@ -1,60 +0,0 @@
/*
* $HeadURL$
* $Revision$
* $Date$
*
* ====================================================================
* 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 org.apache.http.entity.mime;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Iterator;
import org.apache.james.mime4j.message.Header;
/**
* {@link Header} implementation with the stricter RDC 822 compliance.
* To be removed if resolved in mime4j.
*/
class RFC822Header extends Header {
@Override
public void writeTo(final OutputStream out) throws IOException {
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(out, MIME.DEFAULT_CHARSET), 8192);
for (Iterator<?> it = getFields().iterator(); it.hasNext();) {
writer.write(it.next().toString());
writer.write("\r\n");
}
writer.write("\r\n");
writer.flush();
}
}

View File

@ -31,7 +31,7 @@
package org.apache.http.entity.mime.content;
import org.apache.http.entity.mime.ContentDescriptor;
import org.apache.james.mime4j.ContentDescriptor;
import org.apache.james.mime4j.message.Body;
public interface ContentBody extends Body, ContentDescriptor {

View File

@ -36,9 +36,9 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.http.entity.mime.MIME;
import org.apache.james.mime4j.message.AbstractBody;
import org.apache.james.mime4j.message.BinaryBody;
@ -59,13 +59,18 @@ public class FileBody extends AbstractBody implements BinaryBody, ContentBody {
return new FileInputStream(this.file);
}
public void writeTo(final OutputStream out) throws IOException {
public void writeTo(final OutputStream out, int mode) throws IOException {
if (out == null) {
throw new IllegalArgumentException("Output stream may not be null");
}
InputStream in = new FileInputStream(this.file);
try {
IOUtils.copy(in, out);
byte[] tmp = new byte[4096];
int l;
while ((l = in.read(tmp)) != -1) {
out.write(tmp, 0, l);
}
out.flush();
} finally {
in.close();
}
@ -75,7 +80,7 @@ public class FileBody extends AbstractBody implements BinaryBody, ContentBody {
return MIME.ENC_BINARY;
}
public Charset getCharset() {
public String getCharset() {
return null;
}
@ -83,6 +88,18 @@ public class FileBody extends AbstractBody implements BinaryBody, ContentBody {
return "application/octet-stream";
}
public Map<?, ?> getContentTypeParameters() {
return Collections.EMPTY_MAP;
}
public String getMediaType() {
return "application";
}
public String getSubType() {
return "octet-stream";
}
public long getContentLength() {
return this.file.length();
}

View File

@ -34,9 +34,9 @@ package org.apache.http.entity.mime.content;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.http.entity.mime.MIME;
import org.apache.james.mime4j.message.AbstractBody;
import org.apache.james.mime4j.message.BinaryBody;
@ -59,12 +59,17 @@ public class InputStreamBody extends AbstractBody implements BinaryBody, Content
return this.in;
}
public void writeTo(final OutputStream out) throws IOException {
public void writeTo(final OutputStream out, int mode) throws IOException {
if (out == null) {
throw new IllegalArgumentException("Output stream may not be null");
}
try {
IOUtils.copy(this.in, out);
byte[] tmp = new byte[4096];
int l;
while ((l = this.in.read(tmp)) != -1) {
out.write(tmp, 0, l);
}
out.flush();
} finally {
this.in.close();
}
@ -74,7 +79,7 @@ public class InputStreamBody extends AbstractBody implements BinaryBody, Content
return MIME.ENC_BINARY;
}
public Charset getCharset() {
public String getCharset() {
return null;
}
@ -82,6 +87,18 @@ public class InputStreamBody extends AbstractBody implements BinaryBody, Content
return "application/octet-stream";
}
public Map<?, ?> getContentTypeParameters() {
return Collections.EMPTY_MAP;
}
public String getMediaType() {
return "application";
}
public String getSubType() {
return "octet-stream";
}
public long getContentLength() {
return -1;
}

View File

@ -33,13 +33,15 @@ package org.apache.http.entity.mime.content;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.http.entity.mime.MIME;
import org.apache.james.mime4j.message.AbstractBody;
import org.apache.james.mime4j.message.TextBody;
@ -71,25 +73,45 @@ public class StringBody extends AbstractBody implements TextBody, ContentBody {
this.charset);
}
public void writeTo(final OutputStream out) throws IOException {
public void writeTo(final OutputStream out, int mode) throws IOException {
if (out == null) {
throw new IllegalArgumentException("Output stream may not be null");
}
IOUtils.copy(new ByteArrayInputStream(this.content), out);
InputStream in = new ByteArrayInputStream(this.content);
byte[] tmp = new byte[4096];
int l;
while ((l = in.read(tmp)) != -1) {
out.write(tmp, 0, l);
}
out.flush();
}
public String getTransferEncoding() {
return MIME.ENC_8BIT;
}
public Charset getCharset() {
return this.charset;
public String getCharset() {
return this.charset.name();
}
public String getMimeType() {
return "text/plain";
}
public String getMediaType() {
return "text";
}
public String getSubType() {
return "plain";
}
public Map<?, ?> getContentTypeParameters() {
Map<Object, Object> map = new HashMap<Object, Object>();
map.put("charset", this.charset.name());
return map;
}
public long getContentLength() {
return this.content.length;
}

View File

@ -80,7 +80,7 @@ public class TestMultipartForm extends TestCase {
Field.parse("Content-Type: multipart/form-data; boundary=foo"));
message.setHeader(header);
HttpMultipart multipart = new HttpMultipart();
HttpMultipart multipart = new HttpMultipart("form-data");
multipart.setParent(message);
BodyPart p1 = new BodyPart();
Header h1 = new Header();
@ -106,7 +106,7 @@ public class TestMultipartForm extends TestCase {
multipart.writeTo(out);
out.close();
String expected = "\r\n" +
String expected =
"--foo\r\n" +
"Content-Type: text/plain\r\n" +
"\r\n" +
@ -119,8 +119,7 @@ public class TestMultipartForm extends TestCase {
"Content-Type: text/plain\r\n" +
"\r\n" +
"all kind of stuff\r\n" +
"--foo--\r\n" +
"\r\n";
"--foo--\r\n";
String s = out.toString("US-ASCII");
assertEquals(expected, s);
assertEquals(s.length(), multipart.getTotalLength());
@ -133,7 +132,7 @@ public class TestMultipartForm extends TestCase {
Field.parse("Content-Type: multipart/form-data; boundary=foo"));
message.setHeader(header);
HttpMultipart multipart = new HttpMultipart();
HttpMultipart multipart = new HttpMultipart("form-data");
multipart.setParent(message);
FormBodyPart p1 = new FormBodyPart(
"field1",
@ -153,7 +152,7 @@ public class TestMultipartForm extends TestCase {
multipart.writeTo(out);
out.close();
String expected = "\r\n" +
String expected =
"--foo\r\n" +
"Content-Disposition: form-data; name=\"field1\"\r\n" +
"Content-Type: text/plain; charset=" +
@ -174,8 +173,7 @@ public class TestMultipartForm extends TestCase {
"Content-Transfer-Encoding: 8bit\r\n" +
"\r\n" +
"all kind of stuff\r\n" +
"--foo--\r\n" +
"\r\n";
"--foo--\r\n";
String s = out.toString("US-ASCII");
assertEquals(expected, s);
assertEquals(s.length(), multipart.getTotalLength());
@ -197,7 +195,7 @@ public class TestMultipartForm extends TestCase {
writer.close();
}
HttpMultipart multipart = new HttpMultipart();
HttpMultipart multipart = new HttpMultipart("form-data");
multipart.setParent(message);
FormBodyPart p1 = new FormBodyPart(
"field1",
@ -213,7 +211,7 @@ public class TestMultipartForm extends TestCase {
multipart.writeTo(out);
out.close();
String expected = "\r\n" +
String expected =
"--foo\r\n" +
"Content-Disposition: form-data; name=\"field1\"; " +
"filename=\"" + tmpfile.getName() + "\"\r\n" +
@ -228,8 +226,7 @@ public class TestMultipartForm extends TestCase {
"Content-Transfer-Encoding: binary\r\n" +
"\r\n" +
"some random whatever\r\n" +
"--foo--\r\n" +
"\r\n";
"--foo--\r\n";
String s = out.toString("US-ASCII");
assertEquals(expected, s);
assertEquals(-1, multipart.getTotalLength());
@ -253,7 +250,7 @@ public class TestMultipartForm extends TestCase {
writer.close();
}
HttpMultipart multipart = new HttpMultipart();
HttpMultipart multipart = new HttpMultipart("form-data");
multipart.setParent(message);
FormBodyPart p1 = new FormBodyPart(
"field1",
@ -271,7 +268,7 @@ public class TestMultipartForm extends TestCase {
multipart.writeTo(out);
out.close();
String expected = "\r\n" +
String expected =
"--foo\r\n" +
"Content-Disposition: form-data; name=\"field1\"; " +
"filename=\"" + tmpfile.getName() + "\"\r\n" +
@ -282,8 +279,7 @@ public class TestMultipartForm extends TestCase {
"filename=\"file.tmp\"\r\n" +
"\r\n" +
"some random whatever\r\n" +
"--foo--\r\n" +
"\r\n";
"--foo--\r\n";
String s = out.toString("US-ASCII");
assertEquals(expected, s);
assertEquals(-1, multipart.getTotalLength());
@ -329,7 +325,7 @@ public class TestMultipartForm extends TestCase {
writer.close();
}
HttpMultipart multipart = new HttpMultipart();
HttpMultipart multipart = new HttpMultipart("form-data");
multipart.setParent(message);
FormBodyPart p1 = new FormBodyPart(
"field1",
@ -347,7 +343,7 @@ public class TestMultipartForm extends TestCase {
multipart.writeTo(out);
out.close();
String expected = "\r\n" +
String expected =
"--foo\r\n" +
"Content-Disposition: form-data; name=\"field1\"; " +
"filename=\"" + s1 + ".tmp\"\r\n" +
@ -358,8 +354,7 @@ public class TestMultipartForm extends TestCase {
"filename=\"" + s2 + ".tmp\"\r\n" +
"\r\n" +
"some random whatever\r\n" +
"--foo--\r\n" +
"\r\n";
"--foo--\r\n";
String s = out.toString("UTF-8");
assertEquals(expected, s);
assertEquals(-1, multipart.getTotalLength());
@ -377,7 +372,7 @@ public class TestMultipartForm extends TestCase {
Field.parse("Content-Type: multipart/form-data; boundary=foo"));
message.setHeader(header);
HttpMultipart multipart = new HttpMultipart();
HttpMultipart multipart = new HttpMultipart("form-data");
multipart.setParent(message);
FormBodyPart p1 = new FormBodyPart(
"field1",
@ -395,7 +390,7 @@ public class TestMultipartForm extends TestCase {
ByteArrayOutputStream out2 = new ByteArrayOutputStream();
out2.write(("\r\n" +
out2.write((
"--foo\r\n" +
"Content-Disposition: form-data; name=\"field1\"\r\n" +
"Content-Type: text/plain; charset=ISO-8859-1\r\n" +
@ -410,8 +405,7 @@ public class TestMultipartForm extends TestCase {
"\r\n").getBytes("US-ASCII"));
out2.write(s2.getBytes("KOI8-R"));
out2.write(("\r\n" +
"--foo--\r\n" +
"\r\n").getBytes("US-ASCII"));
"--foo--\r\n").getBytes("US-ASCII"));
out2.close();
byte[] actual = out1.toByteArray();

View File

@ -74,7 +74,7 @@
<commons-logging.version>1.1.1</commons-logging.version>
<commons-codec.version>1.3</commons-codec.version>
<commons-io.version>1.2</commons-io.version>
<mime4j.version>0.3</mime4j.version>
<mime4j.version>0.4-SNAPSHOT</mime4j.version>
<junit.version>3.8.2</junit.version>
</properties>