Source code: com/meterware/httpunit/MimeEncodedMessageBody.java
1 package com.meterware.httpunit;
2 /********************************************************************************************************************
3 * $Id: MimeEncodedMessageBody.java,v 1.15 2002/11/15 02:53:59 russgold Exp $
4 *
5 * Copyright (c) 2000-2001, Russell Gold
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
8 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
10 * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in all copies or substantial portions
13 * of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
16 * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
18 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 * DEALINGS IN THE SOFTWARE.
20 *
21 *******************************************************************************************************************/
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.OutputStream;
25
26 /**
27 * A POST-method message body which is MIME-encoded. This is used when uploading files, and is selected when the enctype
28 * parameter of a form is set to "multi-part/form-data".
29 **/
30 class MimeEncodedMessageBody extends MessageBody {
31
32
33 public MimeEncodedMessageBody( PostMethodWebRequest request ) {
34 super( request );
35 }
36
37
38 /**
39 * Returns the content type of this message body.
40 **/
41 String getContentType() {
42 return "multipart/form-data; boundary=" + BOUNDARY;
43 }
44
45
46 /**
47 * Returns the request associated with this message body, cast to a POST request.
48 **/
49 PostMethodWebRequest getPostRequest() {
50 return (PostMethodWebRequest) getRequest();
51 }
52
53
54 /**
55 * Transmits the body of this request as a sequence of bytes.
56 **/
57 void writeTo( OutputStream outputStream ) throws IOException {
58 MimeEncoding encoding = new MimeEncoding( outputStream );
59 getRequest().getParameterHolder().recordParameters( encoding );
60 encoding.sendClose();
61 }
62
63
64 private final static String BOUNDARY = "--HttpUnit-part0-aSgQ2M";
65 private final static byte[] CRLF = { 0x0d, 0x0A };
66
67
68 private String encode( String string ) {
69 char[] chars = string.toCharArray();
70 StringBuffer sb = new StringBuffer(chars.length+20);
71 for (int i = 0; i < chars.length; i++ ) {
72 if (chars[i] == '\\') {
73 sb.append( "\\\\" ); // accomodate MS-DOS file paths ??? is this safe??
74 } else {
75 sb.append( chars[i] );
76 }
77 }
78 return sb.toString();
79 }
80
81
82 private void writeLn( OutputStream os, String value, String encoding ) throws IOException {
83 os.write( value.getBytes( encoding ) );
84 os.write( CRLF );
85 }
86
87
88 private void writeLn( OutputStream os, String value ) throws IOException {
89 writeLn( os, value, getRequest().getCharacterSet() );
90 }
91
92
93 class MimeEncoding implements ParameterProcessor {
94
95 public MimeEncoding( OutputStream outputStream ) {
96 _outputStream = outputStream;
97 }
98
99
100 public void sendClose() throws IOException {
101 writeLn( _outputStream, "--" + BOUNDARY + "--" );
102 }
103
104
105 public void addParameter( String name, String value, String characterSet ) throws IOException {
106 if (name == null || name.length() == 0) return;
107
108 writeLn( _outputStream, "--" + BOUNDARY );
109 writeLn( _outputStream, "Content-Disposition: form-data; name=\"" + name + '"' ); // XXX need to handle non-ascii names here
110 writeLn( _outputStream, "Content-Type: text/plain; charset=" + getRequest().getCharacterSet() );
111 writeLn( _outputStream, "" );
112 writeLn( _outputStream, fixLineEndings( value ), getRequest().getCharacterSet() );
113 }
114
115
116 private final static char CR = 0x0D;
117 private final static char LF = 0x0A;
118
119 private String fixLineEndings( String value ) {
120 StringBuffer sb = new StringBuffer();
121 char[] chars = value.toCharArray();
122 for (int i = 0; i < chars.length; i++) {
123 if (chars[i] == CR || (chars[i] == LF && (i == 0 || chars[i-1] != CR))) {
124 sb.append( CR ).append( LF );
125 } else {
126 sb.append( chars[i] );
127 }
128 }
129 return sb.toString();
130 }
131
132
133 public void addFile( String name, UploadFileSpec spec ) throws IOException {
134 byte[] buffer = new byte[ 8 * 1024 ];
135
136 writeLn( _outputStream, "--" + BOUNDARY );
137 writeLn( _outputStream, "Content-Disposition: form-data; name=\"" + encode( name ) + "\"; filename=\"" + encode( spec.getFileName() ) + '"' ); // XXX need to handle non-ascii names here
138 writeLn( _outputStream, "Content-Type: " + spec.getContentType() );
139 writeLn( _outputStream, "" );
140
141 InputStream in = spec.getInputStream();
142 int count = 0;
143 do {
144 _outputStream.write( buffer, 0, count );
145 count = in.read( buffer, 0, buffer.length );
146 } while (count != -1);
147
148 in.close();
149 writeLn( _outputStream, "" );
150 }
151
152 private OutputStream _outputStream;
153 }
154
155 }
156