    1   /*
    2    * ====================================================================
    3    *
    4    *  Licensed to the Apache Software Foundation (ASF) under one or more
    5    *  contributor license agreements.  See the NOTICE file distributed with
    6    *  this work for additional information regarding copyright ownership.
    7    *  The ASF licenses this file to You under the Apache License, Version 2.0
    8    *  (the "License"); you may not use this file except in compliance with
    9    *  the License.  You may obtain a copy of the License at
    10   *
    11   *      http://www.apache.org/licenses/LICENSE-2.0
    12   *
    13   *  Unless required by applicable law or agreed to in writing, software
    14   *  distributed under the License is distributed on an "AS IS" BASIS,
    15   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16   *  See the License for the specific language governing permissions and
    17   *  limitations under the License.
    18   * ====================================================================
    19   *
    20   * This software consists of voluntary contributions made by many
    21   * individuals on behalf of the Apache Software Foundation.  For more
    22   * information on the Apache Software Foundation, please see
    23   * <http://www.apache.org/>.
    24   *
    25   * [Additional notices, if required by prior licensing conditions]
    26   *
    27   */
    28
    29  package org.apache.commons.httpclient;
    30
    31  import junit.framework.Test;
    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 HttpClientTestBase {
    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(final String testName) throws IOException {
    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 testNoExplicitCharEncoding() throws Exception {
    123         this.server.setHttpService(new EchoService());
    124
    125         GetMethod httpget = new GetMethod("/test/");
    126         httpget.setRequestHeader("Content-Type", "text/plain");
    127         try {
    128             this.client.executeMethod(httpget);
    129             assertEquals(HttpStatus.SC_OK, httpget.getStatusCode());
    130             assertEquals(CHARSET_DEFAULT, httpget.getResponseCharSet());
    131         } finally {
    132             httpget.releaseConnection();
    133         }
    134     }
    135
    136     public void testExplicitCharEncoding() throws Exception {
    137         this.server.setHttpService(new EchoService());
    138
    139         GetMethod httpget = new GetMethod("/test/");
    140         httpget.setRequestHeader("Content-Type", "text/plain; charset=" + CHARSET_UTF8);
    141         try {
    142             this.client.executeMethod(httpget);
    143             assertEquals(HttpStatus.SC_OK, httpget.getStatusCode());
    144             assertEquals(CHARSET_UTF8, httpget.getResponseCharSet());
    145         } finally {
    146             httpget.releaseConnection();
    147         }
    148     }
    149
    150     private String constructString(int [] unicodeChars) {
    151         StringBuffer buffer = new StringBuffer();
    152         if (unicodeChars != null) {
    153             for (int i = 0; i < unicodeChars.length; i++) {
    154                 buffer.append((char)unicodeChars[i]);
    155             }
    156         }
    157         return buffer.toString();
    158     }
    159
    160
    161     private void verifyEncoding(RequestEntity entity, int[] sample)
    162      throws IOException  {
    163
    164         assertNotNull("Request body", entity);
    165
    166         ByteArrayOutputStream bos = new ByteArrayOutputStream();
    167         entity.writeRequest(bos);
    168
    169         InputStream instream = new ByteArrayInputStream(bos.toByteArray());
    170         for (int i = 0; i < sample.length; i++) {
    171             int b = instream.read();
    172             assertTrue("Unexpected end of stream", b != -1);
    173             if (sample[i] != b) {
    174                 fail("Invalid request body encoding");
    175             }
    176         }
    177         assertTrue("End of stream expected", instream.read() == -1);
    178     }
    179
    180     public void testLatinAccentInRequestBody() throws IOException {
    181         PostMethod httppost = new PostMethod("/");
    182         String s = constructString(SWISS_GERMAN_STUFF_UNICODE);
    183         // Test default encoding ISO-8859-1
    184         httppost.setRequestEntity(
    185             new StringRequestEntity(s, "text/plain", CHARSET_DEFAULT));
    186         verifyEncoding(httppost.getRequestEntity(), SWISS_GERMAN_STUFF_ISO8859_1);
    187         // Test UTF-8 encoding
    188         httppost.setRequestEntity(
    189             new StringRequestEntity(s, "text/plain", CHARSET_UTF8));
    190         verifyEncoding(httppost.getRequestEntity(), SWISS_GERMAN_STUFF_UTF8);
    191
    192     }
    193
    194     public void testRussianInRequestBody() throws IOException {
    195         PostMethod httppost = new PostMethod("/");
    196         String s = constructString(RUSSIAN_STUFF_UNICODE);
    197         // Test UTF-8 encoding
    198         httppost.setRequestEntity(
    199             new StringRequestEntity(s, "text/plain", CHARSET_UTF8));
    200         verifyEncoding(httppost.getRequestEntity(), RUSSIAN_STUFF_UTF8);
    201         // Test KOI8-R
    202         httppost.setRequestEntity(
    203             new StringRequestEntity(s, "text/plain", CHARSET_KOI8_R));
    204         verifyEncoding(httppost.getRequestEntity(), RUSSIAN_STUFF_KOI8R);
    205         // Test WIN1251
    206         httppost.setRequestEntity(
    207             new StringRequestEntity(s, "text/plain", CHARSET_WIN1251));
    208         verifyEncoding(httppost.getRequestEntity(), RUSSIAN_STUFF_WIN1251);
    209     }
    210
    211     public void testQueryParams() throws Exception {
    212
    213         GetMethod get = new GetMethod("/");
    214
    215         String ru_msg = constructString(RUSSIAN_STUFF_UNICODE);
    216         String ch_msg = constructString(SWISS_GERMAN_STUFF_UNICODE);
    217
    218         get.setQueryString(new NameValuePair[] {
    219             new NameValuePair("ru", ru_msg),
    220             new NameValuePair("ch", ch_msg)
    221         });
    222
    223         Map params = new HashMap();
    224         StringTokenizer tokenizer = new StringTokenizer(
    225             get.getQueryString(), "&");
    226         while (tokenizer.hasMoreTokens()) {
    227             String s = tokenizer.nextToken();
    228             int i = s.indexOf('=');
    229             assertTrue("Invalid url-encoded parameters", i != -1);
    230             String name = s.substring(0, i).trim();
    231             String value = s.substring(i + 1, s.length()).trim();
    232             value = URIUtil.decode(value, CHARSET_UTF8);
    233             params.put(name, value);
    234         }
    235         assertEquals(ru_msg, params.get("ru"));
    236         assertEquals(ch_msg, params.get("ch"));
    237     }
    238
    239     public void testUrlEncodedRequestBody() throws Exception {
    240
    241         PostMethod httppost = new PostMethod("/");
    242
    243         String ru_msg = constructString(RUSSIAN_STUFF_UNICODE);
    244         String ch_msg = constructString(SWISS_GERMAN_STUFF_UNICODE);
    245
    246         httppost.setRequestBody(new NameValuePair[] {
    247             new NameValuePair("ru", ru_msg),
    248             new NameValuePair("ch", ch_msg)
    249         });
    250
    251         httppost.setRequestHeader("Content-Type", PostMethod.FORM_URL_ENCODED_CONTENT_TYPE
    252             + "; charset=" + CHARSET_UTF8);
    253
    254         ByteArrayOutputStream bos = new ByteArrayOutputStream();
    255         httppost.getRequestEntity().writeRequest(bos);
    256
    257         Map params = new HashMap();
    258         StringTokenizer tokenizer = new StringTokenizer(
    259             new String(bos.toByteArray(), CHARSET_UTF8), "&");
    260         while (tokenizer.hasMoreTokens()) {
    261             String s = tokenizer.nextToken();
    262             int i = s.indexOf('=');
    263             assertTrue("Invalid url-encoded parameters", i != -1);
    264             String name = s.substring(0, i).trim();
    265             String value = s.substring(i + 1, s.length()).trim();
    266             value = URIUtil.decode(value, CHARSET_UTF8);
    267             params.put(name, value);
    268         }
    269         assertEquals(ru_msg, params.get("ru"));
    270         assertEquals(ch_msg, params.get("ch"));
    271     }
    272
    273     public void testRequestEntityLength() throws IOException {
    274         String s = constructString(SWISS_GERMAN_STUFF_UNICODE);
    275         RequestEntity requestentity =
    276             new StringRequestEntity(s, "text/plain", CHARSET_UTF8);
    277         assertEquals(
    278                 s.getBytes(CHARSET_UTF8).length,
    279                 requestentity.getContentLength());
    280     }
    281
    282 }