View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with 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,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.http.impl.client.cache;
28  
29  import static org.junit.Assert.assertFalse;
30  import static org.junit.Assert.assertTrue;
31  
32  import java.io.ByteArrayInputStream;
33  import java.io.ByteArrayOutputStream;
34  import java.io.IOException;
35  import java.io.InputStream;
36  import java.io.ObjectOutputStream;
37  import java.nio.charset.Charset;
38  import java.util.Arrays;
39  import java.util.Date;
40  import java.util.HashMap;
41  import java.util.Map;
42  
43  import org.apache.commons.codec.binary.Base64;
44  import org.apache.http.Header;
45  import org.apache.http.ProtocolVersion;
46  import org.apache.http.StatusLine;
47  import org.apache.http.client.cache.HeaderConstants;
48  import org.apache.http.client.cache.HttpCacheEntry;
49  import org.apache.http.client.cache.HttpCacheEntrySerializationException;
50  import org.apache.http.client.cache.HttpCacheEntrySerializer;
51  import org.apache.http.client.cache.Resource;
52  import org.apache.http.message.BasicHeader;
53  import org.apache.http.message.BasicStatusLine;
54  import org.junit.Before;
55  import org.junit.Test;
56  
57  import com.sun.rowset.JdbcRowSetImpl;
58  
59  public class TestHttpCacheEntrySerializers {
60  
61      private static final Charset UTF8 = Charset.forName("UTF-8");
62  
63      private HttpCacheEntrySerializer impl;
64  
65      @Before
66      public void setUp() {
67          impl = new DefaultHttpCacheEntrySerializer();
68      }
69  
70      @Test
71      public void canSerializeEntriesWithVariantMaps() throws Exception {
72          readWriteVerify(makeCacheEntryWithVariantMap());
73      }
74  
75      @Test
76      public void isAllowedClassNameStringTrue() {
77          assertIsAllowedClassNameTrue(String.class.getName());
78      }
79  
80      @Test
81      public void isAllowedClassNameStringArrayTrue() {
82          assertIsAllowedClassNameTrue("[L" + String.class.getName());
83      }
84  
85      @Test
86      public void isAllowedClassNameStringArrayArrayTrue() {
87          assertIsAllowedClassNameTrue("[[L" + String.class.getName());
88      }
89  
90      @Test
91      public void isAllowedClassNameDataTrue() {
92          assertIsAllowedClassNameTrue(Date.class.getName());
93      }
94  
95      @Test
96      public void isAllowedClassNameStatusLineTrue() {
97          assertIsAllowedClassNameTrue(StatusLine.class.getName());
98      }
99  
100     @Test
101     public void isAllowedClassNameResourceTrue() {
102         assertIsAllowedClassNameTrue(Resource.class.getName());
103     }
104 
105     @Test
106     public void isAllowedClassNameByteArrayTrue() {
107         assertIsAllowedClassNameTrue("[B");
108     }
109 
110     @Test
111     public void isAllowedClassNameByteArrayArrayTrue() {
112         assertIsAllowedClassNameTrue("[[B");
113     }
114 
115     @Test
116     public void isAllowedClassNameCharArrayTrue() {
117         assertIsAllowedClassNameTrue("[C");
118     }
119 
120     @Test
121     public void isAllowedClassNameCharArrayArrayTrue() {
122         assertIsAllowedClassNameTrue("[[C");
123     }
124 
125     @Test
126     public void isAllowedClassNameDoubleArrayTrue() {
127         assertIsAllowedClassNameTrue("[D");
128     }
129 
130     @Test
131     public void isAllowedClassNameDoubleArrayArrayTrue() {
132         assertIsAllowedClassNameTrue("[[D");
133     }
134 
135     @Test
136     public void isAllowedClassNameFloatArrayTrue() {
137         assertIsAllowedClassNameTrue("[F");
138     }
139 
140     @Test
141     public void isAllowedClassNameFloatArrayArrayTrue() {
142         assertIsAllowedClassNameTrue("[[F");
143     }
144 
145     @Test
146     public void isAllowedClassNameIntArrayTrue() {
147         assertIsAllowedClassNameTrue("[I");
148     }
149 
150     @Test
151     public void isAllowedClassNameIntArrayArrayTrue() {
152         assertIsAllowedClassNameTrue("[[I");
153     }
154 
155     @Test
156     public void isAllowedClassNameLongArrayTrue() {
157         assertIsAllowedClassNameTrue("[J");
158     }
159 
160     @Test
161     public void isAllowedClassNameLongArrayArrayTrue() {
162         assertIsAllowedClassNameTrue("[[J");
163     }
164 
165     @Test
166     public void isAllowedClassNameShortArrayTrue() {
167         assertIsAllowedClassNameTrue("[S");
168     }
169 
170     @Test
171     public void isAllowedClassNameShortArrayArrayTrue() {
172         assertIsAllowedClassNameTrue("[[S");
173     }
174 
175     @Test
176     public void isAllowedClassNameCollectionsInvokerTransformerFalse() {
177         assertIsAllowedClassNameFalse("org.apache.commons.collections.functors.InvokerTransformer");
178     }
179 
180     @Test
181     public void isAllowedClassNameCollections4InvokerTransformerFalse() {
182         assertIsAllowedClassNameFalse("org.apache.commons.collections4.functors.InvokerTransformer");
183     }
184 
185     @Test
186     public void isAllowedClassNameCollectionsInstantiateTransformerFalse() {
187         assertIsAllowedClassNameFalse("org.apache.commons.collections.functors.InstantiateTransformer");
188     }
189 
190     @Test
191     public void isAllowedClassNameCollections4InstantiateTransformerFalse() {
192         assertIsAllowedClassNameFalse("org.apache.commons.collections4.functors.InstantiateTransformer");
193     }
194 
195     @Test
196     public void isAllowedClassNameGroovyConvertedClosureFalse() {
197         assertIsAllowedClassNameFalse("org.codehaus.groovy.runtime.ConvertedClosure");
198     }
199 
200     @Test
201     public void isAllowedClassNameGroovyMethodClosureFalse() {
202         assertIsAllowedClassNameFalse("org.codehaus.groovy.runtime.MethodClosure");
203     }
204 
205     @Test
206     public void isAllowedClassNameSpringObjectFactoryFalse() {
207         assertIsAllowedClassNameFalse("org.springframework.beans.factory.ObjectFactory");
208     }
209 
210     @Test
211     public void isAllowedClassNameCalanTemplatesImplFalse() {
212         assertIsAllowedClassNameFalse("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
213     }
214 
215     @Test
216     public void isAllowedClassNameCalanTemplatesImplArrayFalse() {
217         assertIsAllowedClassNameFalse("[Lcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
218     }
219 
220     @Test
221     public void isAllowedClassNameJavaRmiRegistryFalse() {
222         assertIsAllowedClassNameFalse("java.rmi.registry.Registry");
223     }
224 
225     @Test
226     public void isAllowedClassNameJavaRmiServerRemoteObjectInvocationHandlerFalse() {
227         assertIsAllowedClassNameFalse("java.rmi.server.RemoteObjectInvocationHandler");
228     }
229 
230     @Test
231     public void isAllowedClassNameJavaxXmlTransformTemplatesFalse() {
232         assertIsAllowedClassNameFalse("javax.xml.transform.Templates");
233     }
234 
235     @Test
236     public void isAllowedClassNameJavaxManagementMBeanServerInvocationHandlerFalse() {
237         assertIsAllowedClassNameFalse("javax.management.MBeanServerInvocationHandler");
238     }
239 
240     private static void assertIsAllowedClassNameTrue(final String className) {
241         assertTrue(DefaultHttpCacheEntrySerializer.RestrictedObjectInputStream.isAllowedClassName(className));
242     }
243 
244     private static void assertIsAllowedClassNameFalse(final String className) {
245         assertFalse(DefaultHttpCacheEntrySerializer.RestrictedObjectInputStream.isAllowedClassName(className));
246     }
247 
248     @Test(expected = HttpCacheEntrySerializationException.class)
249     public void throwExceptionIfUnsafeDeserialization() throws IOException {
250         impl.readFrom(new ByteArrayInputStream(serializeProhibitedObject()));
251     }
252 
253     private byte[] serializeProhibitedObject() throws IOException {
254         final JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
255         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
256         final ObjectOutputStream oos = new ObjectOutputStream(baos);
257         try {
258             oos.writeObject(jdbcRowSet);
259         } finally {
260             oos.close();
261         }
262         return baos.toByteArray();
263     }
264 
265     private void readWriteVerify(final HttpCacheEntry writeEntry) throws IOException {
266         // write the entry
267         final ByteArrayOutputStream out = new ByteArrayOutputStream();
268         impl.writeTo(writeEntry, out);
269 
270         // read the entry
271         final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
272         final HttpCacheEntry readEntry = impl.readFrom(in);
273 
274         // compare
275         assertTrue(areEqual(readEntry, writeEntry));
276     }
277 
278     private HttpCacheEntry makeCacheEntryWithVariantMap() {
279         final Header[] headers = new Header[5];
280         for (int i = 0; i < headers.length; i++) {
281             headers[i] = new BasicHeader("header" + i, "value" + i);
282         }
283         final String body = "Lorem ipsum dolor sit amet";
284 
285         final ProtocolVersion pvObj = new ProtocolVersion("HTTP", 1, 1);
286         final StatusLine slObj = new BasicStatusLine(pvObj, 200, "ok");
287         final Map<String,String> variantMap = new HashMap<String,String>();
288         variantMap.put("test variant 1","true");
289         variantMap.put("test variant 2","true");
290         final HttpCacheEntry cacheEntry = new HttpCacheEntry(new Date(), new Date(),
291                 slObj, headers, new HeapResource(Base64.decodeBase64(body
292                         .getBytes(UTF8))), variantMap, HeaderConstants.GET_METHOD);
293 
294         return cacheEntry;
295     }
296 
297     private boolean areEqual(final HttpCacheEntry one, final HttpCacheEntry two) throws IOException {
298         // dates are only stored with second precision, so scrub milliseconds
299         if (!((one.getRequestDate().getTime() / 1000) == (two.getRequestDate()
300                 .getTime() / 1000))) {
301             return false;
302         }
303         if (!((one.getResponseDate().getTime() / 1000) == (two
304                 .getResponseDate().getTime() / 1000))) {
305             return false;
306         }
307         if (!one.getProtocolVersion().equals(two.getProtocolVersion())) {
308             return false;
309         }
310 
311         final byte[] onesByteArray = resourceToBytes(one.getResource());
312         final byte[] twosByteArray = resourceToBytes(two.getResource());
313 
314         if (!Arrays.equals(onesByteArray,twosByteArray)) {
315             return false;
316         }
317 
318         final Header[] oneHeaders = one.getAllHeaders();
319         final Header[] twoHeaders = two.getAllHeaders();
320         if (!(oneHeaders.length == twoHeaders.length)) {
321             return false;
322         }
323         for (int i = 0; i < oneHeaders.length; i++) {
324             if (!oneHeaders[i].getName().equals(twoHeaders[i].getName())) {
325                 return false;
326             }
327             if (!oneHeaders[i].getValue().equals(twoHeaders[i].getValue())) {
328                 return false;
329             }
330         }
331 
332         return true;
333     }
334 
335     private byte[] resourceToBytes(final Resource res) throws IOException {
336         final InputStream inputStream = res.getInputStream();
337         final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
338 
339         int readBytes;
340         final byte[] bytes = new byte[8096];
341         while ((readBytes = inputStream.read(bytes)) > 0) {
342             outputStream.write(bytes, 0, readBytes);
343         }
344 
345         final byte[] byteData = outputStream.toByteArray();
346 
347         inputStream.close();
348         outputStream.close();
349 
350         return byteData;
351     }
352 }