1   /*
2    * ====================================================================
3    *
4    *  Copyright 1999-2004 The Apache Software Foundation
5    *
6    *  Licensed under the Apache License, Version 2.0 (the "License");
7    *  you may not use this file except in compliance with the License.
8    *  You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing, software
13   *  distributed under the License is distributed on an "AS IS" BASIS,
14   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   *  See the License for the specific language governing permissions and
16   *  limitations under the License.
17   * ====================================================================
18   *
19   * This software consists of voluntary contributions made by many
20   * individuals on behalf of the Apache Software Foundation.  For more
21   * information on the Apache Software Foundation, please see
22   * <http://www.apache.org/>.
23   *
24   * [Additional notices, if required by prior licensing conditions]
25   *
26   */
27  
28  package org.apache.commons.httpclient;
29  
30  import junit.framework.Test;
31  import junit.framework.TestSuite;
32  
33  import java.io.ByteArrayInputStream;
34  import java.io.ByteArrayOutputStream;
35  import java.io.IOException;
36  import java.io.InputStream;
37  import java.util.HashMap;
38  import java.util.Map;
39  import java.util.StringTokenizer;
40  
41  import org.apache.commons.httpclient.methods.GetMethod;
42  import org.apache.commons.httpclient.methods.PostMethod;
43  import org.apache.commons.httpclient.methods.RequestEntity;
44  import org.apache.commons.httpclient.methods.StringRequestEntity;
45  import org.apache.commons.httpclient.util.URIUtil;
46  
47  /***
48   * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
49   * @author <a href="mailto:ajmas@bigfoot.com">Andre John Mas</a>
50   * @author <a href="mailto:laura@lwerner.org">Laura Werner</a>
51   */
52  
53  public class TestMethodCharEncoding extends HttpClientTestBase {
54  
55      static final String CHARSET_DEFAULT = "ISO-8859-1";
56      static final String CHARSET_ASCII = "US-ASCII";
57      static final String CHARSET_UTF8 = "UTF-8";
58      static final String CHARSET_KOI8_R = "KOI8_R";
59      static final String CHARSET_WIN1251 = "Cp1251";
60  
61      static final int SWISS_GERMAN_STUFF_UNICODE [] = {
62          0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4
63      };
64      
65      static final int SWISS_GERMAN_STUFF_ISO8859_1 [] = {
66          0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4
67      };
68      
69      static final int SWISS_GERMAN_STUFF_UTF8 [] = {
70          0x47, 0x72, 0xC3, 0xBC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xC3, 0xA4,
71          0x6D, 0xC3, 0xA4
72      };
73  
74      static final int RUSSIAN_STUFF_UNICODE [] = {
75          0x412, 0x441, 0x435, 0x43C, 0x5F, 0x43F, 0x440, 0x438, 
76          0x432, 0x435, 0x442 
77      }; 
78  
79      static final int RUSSIAN_STUFF_UTF8 [] = {
80          0xD0, 0x92, 0xD1, 0x81, 0xD0, 0xB5, 0xD0, 0xBC, 0x5F, 
81          0xD0, 0xBF, 0xD1, 0x80, 0xD0, 0xB8, 0xD0, 0xB2, 0xD0, 
82          0xB5, 0xD1, 0x82
83      };
84  
85      static final int RUSSIAN_STUFF_KOI8R [] = {
86          0xF7, 0xD3, 0xC5, 0xCD, 0x5F, 0xD0, 0xD2, 0xC9, 0xD7, 
87          0xC5, 0xD4
88      };
89  
90      static final int RUSSIAN_STUFF_WIN1251 [] = {
91          0xC2, 0xF1, 0xE5, 0xEC, 0x5F, 0xEF, 0xF0, 0xE8, 0xE2, 
92          0xE5, 0xF2
93      };
94  
95      // ------------------------------------------------------------ Constructor
96  
97      public TestMethodCharEncoding(final String testName) throws IOException {
98          super(testName);
99      }
100 
101     // ------------------------------------------------------- TestCase Methods
102 
103     public static Test suite() {
104         return new TestSuite(TestMethodCharEncoding.class);
105     }
106 
107     // ----------------------------------------------------------------- Tests
108 
109 
110     public void testRequestCharEncoding() throws IOException {
111         
112         GetMethod httpget = new GetMethod("/");
113         assertEquals(CHARSET_DEFAULT, httpget.getRequestCharSet());
114         httpget.setRequestHeader("Content-Type", "text/plain; charset=" + CHARSET_ASCII); 
115         assertEquals(CHARSET_ASCII, httpget.getRequestCharSet());
116         httpget.setRequestHeader("Content-Type", "text/plain; charset=" + CHARSET_UTF8); 
117         assertEquals(CHARSET_UTF8, httpget.getRequestCharSet());
118         
119     }
120 
121     public void testNoExplicitCharEncoding() throws Exception {
122         this.server.setHttpService(new EchoService());
123         
124         GetMethod httpget = new GetMethod("/test/");
125         httpget.setRequestHeader("Content-Type", "text/plain"); 
126         try {
127             this.client.executeMethod(httpget);
128             assertEquals(HttpStatus.SC_OK, httpget.getStatusCode());
129             assertEquals(CHARSET_DEFAULT, httpget.getResponseCharSet());
130         } finally {
131             httpget.releaseConnection();
132         }
133     }
134 
135     public void testExplicitCharEncoding() throws Exception {
136         this.server.setHttpService(new EchoService());
137         
138         GetMethod httpget = new GetMethod("/test/");
139         httpget.setRequestHeader("Content-Type", "text/plain; charset=" + CHARSET_UTF8); 
140         try {
141             this.client.executeMethod(httpget);
142             assertEquals(HttpStatus.SC_OK, httpget.getStatusCode());
143             assertEquals(CHARSET_UTF8, httpget.getResponseCharSet());
144         } finally {
145             httpget.releaseConnection();
146         }
147     }
148 
149     private String constructString(int [] unicodeChars) {
150         StringBuffer buffer = new StringBuffer();
151         if (unicodeChars != null) {
152             for (int i = 0; i < unicodeChars.length; i++) {
153                 buffer.append((char)unicodeChars[i]); 
154             }
155         }
156         return buffer.toString();
157     }
158 
159 
160     private void verifyEncoding(RequestEntity entity, int[] sample)
161      throws IOException  {
162         
163         assertNotNull("Request body", entity);
164 
165         ByteArrayOutputStream bos = new ByteArrayOutputStream();
166         entity.writeRequest(bos);
167         
168         InputStream instream = new ByteArrayInputStream(bos.toByteArray());
169         for (int i = 0; i < sample.length; i++) {
170             int b = instream.read();
171             assertTrue("Unexpected end of stream", b != -1);
172             if (sample[i] != b) {
173                 fail("Invalid request body encoding");
174             }
175         }
176         assertTrue("End of stream expected", instream.read() == -1);
177     }
178     
179     public void testLatinAccentInRequestBody() throws IOException {
180         PostMethod httppost = new PostMethod("/");
181         String s = constructString(SWISS_GERMAN_STUFF_UNICODE);
182         // Test default encoding ISO-8859-1
183         httppost.setRequestEntity(
184             new StringRequestEntity(s, "text/plain", CHARSET_DEFAULT));
185         verifyEncoding(httppost.getRequestEntity(), SWISS_GERMAN_STUFF_ISO8859_1);
186         // Test UTF-8 encoding
187         httppost.setRequestEntity(
188             new StringRequestEntity(s, "text/plain", CHARSET_UTF8));
189         verifyEncoding(httppost.getRequestEntity(), SWISS_GERMAN_STUFF_UTF8);
190 
191     }
192     
193     public void testRussianInRequestBody() throws IOException {
194         PostMethod httppost = new PostMethod("/");
195         String s = constructString(RUSSIAN_STUFF_UNICODE);
196         // Test UTF-8 encoding
197         httppost.setRequestEntity(
198             new StringRequestEntity(s, "text/plain", CHARSET_UTF8));
199         verifyEncoding(httppost.getRequestEntity(), RUSSIAN_STUFF_UTF8);
200         // Test KOI8-R
201         httppost.setRequestEntity(
202             new StringRequestEntity(s, "text/plain", CHARSET_KOI8_R));
203         verifyEncoding(httppost.getRequestEntity(), RUSSIAN_STUFF_KOI8R);
204         // Test WIN1251
205         httppost.setRequestEntity(
206             new StringRequestEntity(s, "text/plain", CHARSET_WIN1251));
207         verifyEncoding(httppost.getRequestEntity(), RUSSIAN_STUFF_WIN1251);
208     }
209 
210     public void testQueryParams() throws Exception {
211 
212         GetMethod get = new GetMethod("/");
213 
214         String ru_msg = constructString(RUSSIAN_STUFF_UNICODE); 
215         String ch_msg = constructString(SWISS_GERMAN_STUFF_UNICODE); 
216 
217         get.setQueryString(new NameValuePair[] {
218             new NameValuePair("ru", ru_msg),
219             new NameValuePair("ch", ch_msg) 
220         });            
221 
222         Map params = new HashMap();
223         StringTokenizer tokenizer = new StringTokenizer(
224             get.getQueryString(), "&");
225         while (tokenizer.hasMoreTokens()) {
226             String s = tokenizer.nextToken();
227             int i = s.indexOf('=');
228             assertTrue("Invalid url-encoded parameters", i != -1);
229             String name = s.substring(0, i).trim(); 
230             String value = s.substring(i + 1, s.length()).trim(); 
231             value = URIUtil.decode(value, CHARSET_UTF8);
232             params.put(name, value);
233         }
234         assertEquals(ru_msg, params.get("ru"));
235         assertEquals(ch_msg, params.get("ch"));
236     }
237 
238     public void testUrlEncodedRequestBody() throws Exception {
239 
240         PostMethod httppost = new PostMethod("/");
241 
242         String ru_msg = constructString(RUSSIAN_STUFF_UNICODE); 
243         String ch_msg = constructString(SWISS_GERMAN_STUFF_UNICODE); 
244 
245         httppost.setRequestBody(new NameValuePair[] {
246             new NameValuePair("ru", ru_msg),
247             new NameValuePair("ch", ch_msg) 
248         });            
249 
250         httppost.setRequestHeader("Content-Type", PostMethod.FORM_URL_ENCODED_CONTENT_TYPE 
251             + "; charset=" + CHARSET_UTF8);
252 
253         ByteArrayOutputStream bos = new ByteArrayOutputStream();
254         httppost.getRequestEntity().writeRequest(bos);
255         
256         Map params = new HashMap();
257         StringTokenizer tokenizer = new StringTokenizer(
258             new String(bos.toByteArray(), CHARSET_UTF8), "&");
259         while (tokenizer.hasMoreTokens()) {
260             String s = tokenizer.nextToken();
261             int i = s.indexOf('=');
262             assertTrue("Invalid url-encoded parameters", i != -1);
263             String name = s.substring(0, i).trim(); 
264             String value = s.substring(i + 1, s.length()).trim(); 
265             value = URIUtil.decode(value, CHARSET_UTF8);
266             params.put(name, value);
267         }
268         assertEquals(ru_msg, params.get("ru"));
269         assertEquals(ch_msg, params.get("ch"));
270     }
271     
272     public void testRequestEntityLength() throws IOException {
273         String s = constructString(SWISS_GERMAN_STUFF_UNICODE);
274         RequestEntity requestentity =
275             new StringRequestEntity(s, "text/plain", CHARSET_UTF8);
276         assertEquals(
277                 s.getBytes(CHARSET_UTF8).length, 
278                 requestentity.getContentLength()); 
279     }
280     
281 }