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.TestCase;
32  import junit.framework.TestSuite;
33  
34  import java.io.ByteArrayInputStream;
35  import java.io.ByteArrayOutputStream;
36  import java.io.IOException;
37  import java.io.InputStream;
38  import java.util.HashMap;
39  import java.util.Map;
40  import java.util.StringTokenizer;
41  
42  import org.apache.commons.httpclient.methods.GetMethod;
43  import org.apache.commons.httpclient.methods.PostMethod;
44  import org.apache.commons.httpclient.methods.RequestEntity;
45  import org.apache.commons.httpclient.methods.StringRequestEntity;
46  import org.apache.commons.httpclient.util.URIUtil;
47  
48  /***
49   * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
50   * @author <a href="mailto:ajmas@bigfoot.com">Andre John Mas</a>
51   * @author <a href="mailto:laura@lwerner.org">Laura Werner</a>
52   */
53  
54  public class TestMethodCharEncoding extends TestCase {
55  
56      static final String CHARSET_DEFAULT = "ISO-8859-1";
57      static final String CHARSET_ASCII = "US-ASCII";
58      static final String CHARSET_UTF8 = "UTF-8";
59      static final String CHARSET_KOI8_R = "KOI8_R";
60      static final String CHARSET_WIN1251 = "Cp1251";
61  
62      static final int SWISS_GERMAN_STUFF_UNICODE [] = {
63          0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4
64      };
65      
66      static final int SWISS_GERMAN_STUFF_ISO8859_1 [] = {
67          0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4
68      };
69      
70      static final int SWISS_GERMAN_STUFF_UTF8 [] = {
71          0x47, 0x72, 0xC3, 0xBC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xC3, 0xA4,
72          0x6D, 0xC3, 0xA4
73      };
74  
75      static final int RUSSIAN_STUFF_UNICODE [] = {
76          0x412, 0x441, 0x435, 0x43C, 0x5F, 0x43F, 0x440, 0x438, 
77          0x432, 0x435, 0x442 
78      }; 
79  
80      static final int RUSSIAN_STUFF_UTF8 [] = {
81          0xD0, 0x92, 0xD1, 0x81, 0xD0, 0xB5, 0xD0, 0xBC, 0x5F, 
82          0xD0, 0xBF, 0xD1, 0x80, 0xD0, 0xB8, 0xD0, 0xB2, 0xD0, 
83          0xB5, 0xD1, 0x82
84      };
85  
86      static final int RUSSIAN_STUFF_KOI8R [] = {
87          0xF7, 0xD3, 0xC5, 0xCD, 0x5F, 0xD0, 0xD2, 0xC9, 0xD7, 
88          0xC5, 0xD4
89      };
90  
91      static final int RUSSIAN_STUFF_WIN1251 [] = {
92          0xC2, 0xF1, 0xE5, 0xEC, 0x5F, 0xEF, 0xF0, 0xE8, 0xE2, 
93          0xE5, 0xF2
94      };
95  
96      // ------------------------------------------------------------ Constructor
97  
98      public TestMethodCharEncoding(String testName) {
99          super(testName);
100     }
101 
102     // ------------------------------------------------------- TestCase Methods
103 
104     public static Test suite() {
105         return new TestSuite(TestMethodCharEncoding.class);
106     }
107 
108     // ----------------------------------------------------------------- Tests
109 
110 
111     public void testRequestCharEncoding() throws IOException {
112         
113         GetMethod httpget = new GetMethod("/");
114         assertEquals(CHARSET_DEFAULT, httpget.getRequestCharSet());
115         httpget.setRequestHeader("Content-Type", "text/plain; charset=" + CHARSET_ASCII); 
116         assertEquals(CHARSET_ASCII, httpget.getRequestCharSet());
117         httpget.setRequestHeader("Content-Type", "text/plain; charset=" + CHARSET_UTF8); 
118         assertEquals(CHARSET_UTF8, httpget.getRequestCharSet());
119         
120     }
121 
122     public void testResponseCharEncoding() throws Exception {
123         
124         SimpleHttpConnection conn = new SimpleHttpConnection();
125         String body = "stuff";
126         String headers1 = "HTTP/1.1 200 OK\r\n"
127                        +"Content-Length: 5\r\n";
128         conn.addResponse(headers1, body);
129         conn.open();
130         GetMethod httpget = new GetMethod("/");
131         httpget.execute(new HttpState(), conn);
132         assertEquals(CHARSET_DEFAULT, httpget.getResponseCharSet());
133         conn.close();
134         
135         httpget = new GetMethod("/");
136         String headers2 = "HTTP/1.1 200 OK\r\n"
137                        +"Content-Type: text/plain\r\n"
138                        +"Content-Length: 5\r\n";
139         conn.addResponse(headers2, body);
140         conn.open();
141         httpget.execute(new HttpState(), conn);
142         assertEquals(CHARSET_DEFAULT, httpget.getResponseCharSet());
143         conn.close();
144 
145         httpget = new GetMethod("/");
146         String headers3 = "HTTP/1.1 200 OK\r\n"
147                        +"Content-Type: text/plain; charset=" + CHARSET_UTF8 + "\r\n"
148                        +"Content-Length: 5\r\n";
149         conn.addResponse(headers3, body);
150         conn.open();
151         httpget.execute(new HttpState(), conn);
152         assertEquals(CHARSET_UTF8, httpget.getResponseCharSet());
153         conn.close();
154     }
155 
156 
157     private String constructString(int [] unicodeChars) {
158         StringBuffer buffer = new StringBuffer();
159         if (unicodeChars != null) {
160             for (int i = 0; i < unicodeChars.length; i++) {
161                 buffer.append((char)unicodeChars[i]); 
162             }
163         }
164         return buffer.toString();
165     }
166 
167 
168     private void verifyEncoding(RequestEntity entity, int[] sample)
169      throws IOException  {
170         
171         assertNotNull("Request body", entity);
172 
173         ByteArrayOutputStream bos = new ByteArrayOutputStream();
174         entity.writeRequest(bos);
175         
176         InputStream instream = new ByteArrayInputStream(bos.toByteArray());
177         for (int i = 0; i < sample.length; i++) {
178             int b = instream.read();
179             assertTrue("Unexpected end of stream", b != -1);
180             if (sample[i] != b) {
181                 fail("Invalid request body encoding");
182             }
183         }
184         assertTrue("End of stream expected", instream.read() == -1);
185     }
186     
187     public void testLatinAccentInRequestBody() throws IOException {
188         PostMethod httppost = new PostMethod("/");
189         String s = constructString(SWISS_GERMAN_STUFF_UNICODE);
190         // Test default encoding ISO-8859-1
191         httppost.setRequestEntity(
192             new StringRequestEntity(s, "text/plain", CHARSET_DEFAULT));
193         verifyEncoding(httppost.getRequestEntity(), SWISS_GERMAN_STUFF_ISO8859_1);
194         // Test UTF-8 encoding
195         httppost.setRequestEntity(
196             new StringRequestEntity(s, "text/plain", CHARSET_UTF8));
197         verifyEncoding(httppost.getRequestEntity(), SWISS_GERMAN_STUFF_UTF8);
198 
199     }
200     
201     public void testRussianInRequestBody() throws IOException {
202         PostMethod httppost = new PostMethod("/");
203         String s = constructString(RUSSIAN_STUFF_UNICODE);
204         // Test UTF-8 encoding
205         httppost.setRequestEntity(
206             new StringRequestEntity(s, "text/plain", CHARSET_UTF8));
207         verifyEncoding(httppost.getRequestEntity(), RUSSIAN_STUFF_UTF8);
208         // Test KOI8-R
209         httppost.setRequestEntity(
210             new StringRequestEntity(s, "text/plain", CHARSET_KOI8_R));
211         verifyEncoding(httppost.getRequestEntity(), RUSSIAN_STUFF_KOI8R);
212         // Test WIN1251
213         httppost.setRequestEntity(
214             new StringRequestEntity(s, "text/plain", CHARSET_WIN1251));
215         verifyEncoding(httppost.getRequestEntity(), RUSSIAN_STUFF_WIN1251);
216     }
217 
218     public void testQueryParams() throws Exception {
219 
220         GetMethod get = new GetMethod("/");
221 
222         String ru_msg = constructString(RUSSIAN_STUFF_UNICODE); 
223         String ch_msg = constructString(SWISS_GERMAN_STUFF_UNICODE); 
224 
225         get.setQueryString(new NameValuePair[] {
226             new NameValuePair("ru", ru_msg),
227             new NameValuePair("ch", ch_msg) 
228         });            
229 
230         Map params = new HashMap();
231         StringTokenizer tokenizer = new StringTokenizer(
232             get.getQueryString(), "&");
233         while (tokenizer.hasMoreTokens()) {
234             String s = tokenizer.nextToken();
235             int i = s.indexOf('=');
236             assertTrue("Invalid url-encoded parameters", i != -1);
237             String name = s.substring(0, i).trim(); 
238             String value = s.substring(i + 1, s.length()).trim(); 
239             value = URIUtil.decode(value, CHARSET_UTF8);
240             params.put(name, value);
241         }
242         assertEquals(ru_msg, params.get("ru"));
243         assertEquals(ch_msg, params.get("ch"));
244     }
245 
246     public void testUrlEncodedRequestBody() throws Exception {
247 
248         PostMethod httppost = new PostMethod("/");
249 
250         String ru_msg = constructString(RUSSIAN_STUFF_UNICODE); 
251         String ch_msg = constructString(SWISS_GERMAN_STUFF_UNICODE); 
252 
253         httppost.setRequestBody(new NameValuePair[] {
254             new NameValuePair("ru", ru_msg),
255             new NameValuePair("ch", ch_msg) 
256         });            
257 
258         httppost.setRequestHeader("Content-Type", PostMethod.FORM_URL_ENCODED_CONTENT_TYPE 
259             + "; charset=" + CHARSET_UTF8);
260 
261         ByteArrayOutputStream bos = new ByteArrayOutputStream();
262         httppost.getRequestEntity().writeRequest(bos);
263         
264         Map params = new HashMap();
265         StringTokenizer tokenizer = new StringTokenizer(
266             new String(bos.toByteArray(), CHARSET_UTF8), "&");
267         while (tokenizer.hasMoreTokens()) {
268             String s = tokenizer.nextToken();
269             int i = s.indexOf('=');
270             assertTrue("Invalid url-encoded parameters", i != -1);
271             String name = s.substring(0, i).trim(); 
272             String value = s.substring(i + 1, s.length()).trim(); 
273             value = URIUtil.decode(value, CHARSET_UTF8);
274             params.put(name, value);
275         }
276         assertEquals(ru_msg, params.get("ru"));
277         assertEquals(ch_msg, params.get("ch"));
278     }
279     
280 }