1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.client5.http.impl.cache;
28
29 import java.io.InputStream;
30 import java.util.Date;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.Map;
34 import java.util.Random;
35
36 import org.apache.hc.client5.http.cache.HttpCacheEntry;
37 import org.apache.hc.client5.http.utils.DateUtils;
38 import org.apache.hc.core5.http.ClassicHttpRequest;
39 import org.apache.hc.core5.http.ClassicHttpResponse;
40 import org.apache.hc.core5.http.Header;
41 import org.apache.hc.core5.http.HeaderElement;
42 import org.apache.hc.core5.http.HttpEntity;
43 import org.apache.hc.core5.http.HttpHeaders;
44 import org.apache.hc.core5.http.HttpMessage;
45 import org.apache.hc.core5.http.HttpRequest;
46 import org.apache.hc.core5.http.HttpResponse;
47 import org.apache.hc.core5.http.HttpStatus;
48 import org.apache.hc.core5.http.HttpVersion;
49 import org.apache.hc.core5.http.Method;
50 import org.apache.hc.core5.http.ProtocolVersion;
51 import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
52 import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
53 import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
54 import org.apache.hc.core5.http.message.BasicHeader;
55 import org.apache.hc.core5.http.message.MessageSupport;
56 import org.apache.hc.core5.util.ByteArrayBuffer;
57 import org.apache.hc.core5.util.LangUtils;
58 import org.junit.Assert;
59
60 public class HttpTestUtils {
61
62
63
64
65
66
67 private static final String[] HOP_BY_HOP_HEADERS = { "Connection", "Keep-Alive", "Proxy-Authenticate",
68 "Proxy-Authorization", "TE", "Trailers", "Transfer-Encoding", "Upgrade" };
69
70
71
72
73
74
75
76
77 private static final String[] MULTI_HEADERS = { "Accept", "Accept-Charset", "Accept-Encoding",
78 "Accept-Language", "Allow", "Cache-Control", "Connection", "Content-Encoding",
79 "Content-Language", "Expect", "Pragma", "Proxy-Authenticate", "TE", "Trailer",
80 "Transfer-Encoding", "Upgrade", "Via", HttpHeaders.WARNING, "WWW-Authenticate" };
81 private static final String[] SINGLE_HEADERS = { "Accept-Ranges", "Age", "Authorization",
82 "Content-Length", "Content-Location", "Content-MD5", "Content-Range", "Content-Type",
83 "Date", "ETag", "Expires", "From", "Host", "If-Match", "If-Modified-Since",
84 "If-None-Match", "If-Range", "If-Unmodified-Since", "Last-Modified", "Location",
85 "Max-Forwards", "Proxy-Authorization", "Range", "Referer", "Retry-After", "Server",
86 "User-Agent", "Vary" };
87
88
89
90
91
92
93
94 public static boolean isHopByHopHeader(final String name) {
95 for (final String s : HOP_BY_HOP_HEADERS) {
96 if (s.equalsIgnoreCase(name)) {
97 return true;
98 }
99 }
100 return false;
101 }
102
103
104
105
106 public static boolean isSingleHeader(final String name) {
107 for (final String s : SINGLE_HEADERS) {
108 if (s.equalsIgnoreCase(name)) {
109 return true;
110 }
111 }
112 return false;
113 }
114
115
116
117 public static boolean equivalent(final HttpEntity e1, final HttpEntity e2) throws Exception {
118 final InputStream i1 = e1.getContent();
119 final InputStream i2 = e2.getContent();
120 if (i1 == null && i2 == null) {
121 return true;
122 }
123 if (i1 == null || i2 == null) {
124 return false;
125 }
126 int b1 = -1;
127 while ((b1 = i1.read()) != -1) {
128 if (b1 != i2.read()) {
129 return false;
130 }
131 }
132 return (-1 == i2.read());
133 }
134
135
136
137
138
139
140
141 public static String getCanonicalHeaderValue(final HttpMessage r, final String name) {
142 if (isSingleHeader(name)) {
143 final Header h = r.getFirstHeader(name);
144 return (h != null) ? h.getValue() : null;
145 }
146 final StringBuilder buf = new StringBuilder();
147 boolean first = true;
148 for (final Header h : r.getHeaders(name)) {
149 if (!first) {
150 buf.append(", ");
151 }
152 buf.append(h.getValue().trim());
153 first = false;
154 }
155 return buf.toString();
156 }
157
158
159
160
161
162 public static boolean isEndToEndHeaderSubset(final HttpMessage r1, final HttpMessage r2) {
163 for (final Header h : r1.getHeaders()) {
164 if (!isHopByHopHeader(h.getName())) {
165 final String r1val = getCanonicalHeaderValue(r1, h.getName());
166 final String r2val = getCanonicalHeaderValue(r2, h.getName());
167 if (!r1val.equals(r2val)) {
168 return false;
169 }
170 }
171 }
172 return true;
173 }
174
175
176
177
178
179
180
181
182
183
184 public static boolean semanticallyTransparent(
185 final ClassicHttpResponse r1, final ClassicHttpResponse r2) throws Exception {
186 final boolean entitiesEquivalent = equivalent(r1.getEntity(), r2.getEntity());
187 if (!entitiesEquivalent) {
188 return false;
189 }
190 final boolean statusLinesEquivalent = LangUtils.equals(r1.getReasonPhrase(), r2.getReasonPhrase())
191 && r1.getCode() == r2.getCode();
192 if (!statusLinesEquivalent) {
193 return false;
194 }
195 return isEndToEndHeaderSubset(r1, r2);
196 }
197
198
199 public static boolean equivalent(final ProtocolVersion v1, final ProtocolVersion v2) {
200 return LangUtils.equals(v1 != null ? v1 : HttpVersion.DEFAULT, v2 != null ? v2 : HttpVersion.DEFAULT );
201 }
202
203
204 public static boolean equivalent(final HttpRequest r1, final HttpRequest r2) {
205 return equivalent(r1.getVersion(), r2.getVersion()) &&
206 LangUtils.equals(r1.getMethod(), r2.getMethod()) &&
207 LangUtils.equals(r1.getRequestUri(), r2.getRequestUri()) &&
208 isEndToEndHeaderSubset(r1, r2);
209 }
210
211
212 public static boolean equivalent(final HttpResponse r1, final HttpResponse r2) {
213 return equivalent(r1.getVersion(), r2.getVersion()) &&
214 r1.getCode() == r2.getCode() &&
215 LangUtils.equals(r1.getReasonPhrase(), r2.getReasonPhrase()) &&
216 isEndToEndHeaderSubset(r1, r2);
217 }
218
219 public static byte[] getRandomBytes(final int nbytes) {
220 final byte[] bytes = new byte[nbytes];
221 new Random().nextBytes(bytes);
222 return bytes;
223 }
224
225 public static ByteArrayBuffer getRandomBuffer(final int nbytes) {
226 final ByteArrayBuffer buf = new ByteArrayBuffer(nbytes);
227 buf.setLength(nbytes);
228 new Random().nextBytes(buf.array());
229 return buf;
230 }
231
232
233
234
235
236 public static HttpEntity makeBody(final int nbytes) {
237 return new ByteArrayEntity(getRandomBytes(nbytes), null);
238 }
239
240 public static HttpCacheEntry makeCacheEntry(final Date requestDate, final Date responseDate) {
241 final Date when = new Date((responseDate.getTime() + requestDate.getTime()) / 2);
242 return makeCacheEntry(requestDate, responseDate, getStockHeaders(when));
243 }
244
245 public static Header[] getStockHeaders(final Date when) {
246 final Header[] headers = {
247 new BasicHeader("Date", DateUtils.formatDate(when)),
248 new BasicHeader("Server", "MockServer/1.0")
249 };
250 return headers;
251 }
252
253 public static HttpCacheEntry makeCacheEntry(final Date requestDate,
254 final Date responseDate, final Header[] headers) {
255 final byte[] bytes = getRandomBytes(128);
256 return makeCacheEntry(requestDate, responseDate, headers, bytes);
257 }
258
259 public static HttpCacheEntry makeCacheEntry(final Date requestDate,
260 final Date responseDate, final Header[] headers, final byte[] bytes) {
261 final Map<String,String> variantMap = null;
262 return makeCacheEntry(requestDate, responseDate, headers, bytes,
263 variantMap);
264 }
265
266 public static HttpCacheEntry makeCacheEntry(final Map<String,String> variantMap) {
267 final Date now = new Date();
268 return makeCacheEntry(now, now, getStockHeaders(now),
269 getRandomBytes(128), variantMap);
270 }
271
272 public static HttpCacheEntry makeCacheEntry(final Date requestDate,
273 final Date responseDate, final Header[] headers, final byte[] bytes,
274 final Map<String,String> variantMap) {
275 return new HttpCacheEntry(requestDate, responseDate, HttpStatus.SC_OK, headers, new HeapResource(bytes), variantMap);
276 }
277
278 public static HttpCacheEntry makeCacheEntry(final Header[] headers, final byte[] bytes) {
279 final Date now = new Date();
280 return makeCacheEntry(now, now, headers, bytes);
281 }
282
283 public static HttpCacheEntry makeCacheEntry(final byte[] bytes) {
284 return makeCacheEntry(getStockHeaders(new Date()), bytes);
285 }
286
287 public static HttpCacheEntry makeCacheEntry(final Header[] headers) {
288 return makeCacheEntry(headers, getRandomBytes(128));
289 }
290
291 public static HttpCacheEntry makeCacheEntry() {
292 final Date now = new Date();
293 return makeCacheEntry(now, now);
294 }
295
296 public static HttpCacheEntry makeCacheEntryWithNoRequestMethodOrEntity(final Header[] headers) {
297 final Date now = new Date();
298 return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, null, null);
299 }
300
301 public static HttpCacheEntry makeCacheEntryWithNoRequestMethod(final Header[] headers) {
302 final Date now = new Date();
303 return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, new HeapResource(getRandomBytes(128)), null);
304 }
305
306 public static HttpCacheEntry make204CacheEntryWithNoRequestMethod(final Header[] headers) {
307 final Date now = new Date();
308 return new HttpCacheEntry(now, now, HttpStatus.SC_NO_CONTENT, headers, null, null);
309 }
310
311 public static HttpCacheEntry makeHeadCacheEntry(final Header[] headers) {
312 final Date now = new Date();
313 return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, null, null);
314 }
315
316 public static HttpCacheEntry makeHeadCacheEntryWithNoRequestMethod(final Header[] headers) {
317 final Date now = new Date();
318 return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, null, null);
319 }
320
321 public static ClassicHttpResponse make200Response() {
322 final ClassicHttpResponse out = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
323 out.setHeader("Date", DateUtils.formatDate(new Date()));
324 out.setHeader("Server", "MockOrigin/1.0");
325 out.setHeader("Content-Length", "128");
326 out.setEntity(makeBody(128));
327 return out;
328 }
329
330 public static final ClassicHttpResponse make200Response(final Date date, final String cacheControl) {
331 final ClassicHttpResponse response = HttpTestUtils.make200Response();
332 response.setHeader("Date", DateUtils.formatDate(date));
333 response.setHeader("Cache-Control",cacheControl);
334 response.setHeader("Etag","\"etag\"");
335 return response;
336 }
337
338 public static ClassicHttpResponse make304Response() {
339 return new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not modified");
340 }
341
342 public static final void assert110WarningFound(final HttpResponse response) {
343 boolean found110Warning = false;
344 final Iterator<HeaderElement> it = MessageSupport.iterate(response, HttpHeaders.WARNING);
345 while (it.hasNext()) {
346 final HeaderElement elt = it.next();
347 final String[] parts = elt.getName().split("\\s");
348 if ("110".equals(parts[0])) {
349 found110Warning = true;
350 break;
351 }
352 }
353 Assert.assertTrue(found110Warning);
354 }
355
356 public static ClassicHttpRequest makeDefaultRequest() {
357 return new BasicClassicHttpRequest(Method.GET.toString(), "/");
358 }
359
360 public static ClassicHttpRequest makeDefaultHEADRequest() {
361 return new BasicClassicHttpRequest(Method.HEAD.toString(), "/");
362 }
363
364 public static ClassicHttpResponse make500Response() {
365 return new BasicClassicHttpResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Internal Server Error");
366 }
367
368 public static Map<String, String> makeDefaultVariantMap(final String key, final String value) {
369 final Map<String, String> variants = new HashMap<>();
370 variants.put(key, value);
371
372 return variants;
373 }
374 }