OLD | NEW |
---|---|
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.net.test.util; | 5 package org.chromium.net.test.util; |
6 | 6 |
7 import android.annotation.SuppressLint; | 7 import android.annotation.SuppressLint; |
8 import android.util.Base64; | 8 import android.util.Base64; |
9 import android.util.Log; | 9 import android.util.Log; |
10 import android.util.Pair; | 10 import android.util.Pair; |
11 | 11 |
12 import org.apache.http.Header; | |
12 import org.apache.http.HttpException; | 13 import org.apache.http.HttpException; |
13 import org.apache.http.HttpRequest; | 14 import org.apache.http.HttpRequest; |
14 import org.apache.http.HttpResponse; | 15 import org.apache.http.HttpResponse; |
15 import org.apache.http.HttpStatus; | 16 import org.apache.http.HttpStatus; |
16 import org.apache.http.HttpVersion; | 17 import org.apache.http.HttpVersion; |
17 import org.apache.http.RequestLine; | 18 import org.apache.http.RequestLine; |
18 import org.apache.http.StatusLine; | 19 import org.apache.http.StatusLine; |
19 import org.apache.http.entity.ByteArrayEntity; | 20 import org.apache.http.entity.ByteArrayEntity; |
20 import org.apache.http.impl.DefaultHttpServerConnection; | 21 import org.apache.http.impl.DefaultHttpServerConnection; |
21 import org.apache.http.impl.cookie.DateUtils; | 22 import org.apache.http.impl.cookie.DateUtils; |
22 import org.apache.http.message.BasicHttpResponse; | 23 import org.apache.http.message.BasicHttpResponse; |
23 import org.apache.http.params.BasicHttpParams; | 24 import org.apache.http.params.BasicHttpParams; |
24 import org.apache.http.params.CoreProtocolPNames; | 25 import org.apache.http.params.CoreProtocolPNames; |
25 import org.apache.http.params.HttpParams; | 26 import org.apache.http.params.HttpParams; |
26 | 27 |
27 import java.io.ByteArrayInputStream; | 28 import java.io.ByteArrayInputStream; |
28 import java.io.IOException; | 29 import java.io.IOException; |
29 import java.io.InputStream; | 30 import java.io.InputStream; |
30 import java.net.MalformedURLException; | 31 import java.net.MalformedURLException; |
31 import java.net.ServerSocket; | 32 import java.net.ServerSocket; |
32 import java.net.Socket; | 33 import java.net.Socket; |
33 import java.net.URI; | 34 import java.net.URI; |
34 import java.net.URL; | 35 import java.net.URL; |
35 import java.net.URLConnection; | 36 import java.net.URLConnection; |
37 import java.nio.charset.Charset; | |
36 import java.security.KeyManagementException; | 38 import java.security.KeyManagementException; |
37 import java.security.KeyStore; | 39 import java.security.KeyStore; |
40 import java.security.MessageDigest; | |
38 import java.security.NoSuchAlgorithmException; | 41 import java.security.NoSuchAlgorithmException; |
39 import java.security.cert.X509Certificate; | 42 import java.security.cert.X509Certificate; |
40 import java.util.ArrayList; | 43 import java.util.ArrayList; |
41 import java.util.Date; | 44 import java.util.Date; |
42 import java.util.HashMap; | 45 import java.util.HashMap; |
43 import java.util.Hashtable; | 46 import java.util.Hashtable; |
44 import java.util.List; | 47 import java.util.List; |
45 import java.util.Map; | 48 import java.util.Map; |
46 | 49 |
47 import javax.net.ssl.HostnameVerifier; | 50 import javax.net.ssl.HostnameVerifier; |
(...skipping 25 matching lines...) Expand all Loading... | |
73 private final boolean mSsl; | 76 private final boolean mSsl; |
74 private final int mPort; | 77 private final int mPort; |
75 | 78 |
76 private static class Response { | 79 private static class Response { |
77 final byte[] mResponseData; | 80 final byte[] mResponseData; |
78 final List<Pair<String, String>> mResponseHeaders; | 81 final List<Pair<String, String>> mResponseHeaders; |
79 final boolean mIsRedirect; | 82 final boolean mIsRedirect; |
80 final Runnable mResponseAction; | 83 final Runnable mResponseAction; |
81 final boolean mIsNotFound; | 84 final boolean mIsNotFound; |
82 final boolean mIsNoContent; | 85 final boolean mIsNoContent; |
86 final boolean mForWebSocket; | |
83 | 87 |
84 Response(byte[] responseData, List<Pair<String, String>> responseHeaders , | 88 Response(byte[] responseData, List<Pair<String, String>> responseHeaders , |
85 boolean isRedirect, boolean isNotFound, boolean isNoContent, | 89 boolean isRedirect, boolean isNotFound, boolean isNoContent, boo lean forWebSocket, |
86 Runnable responseAction) { | 90 Runnable responseAction) { |
87 mIsRedirect = isRedirect; | 91 mIsRedirect = isRedirect; |
88 mIsNotFound = isNotFound; | 92 mIsNotFound = isNotFound; |
89 mIsNoContent = isNoContent; | 93 mIsNoContent = isNoContent; |
94 mForWebSocket = forWebSocket; | |
90 mResponseData = responseData; | 95 mResponseData = responseData; |
91 mResponseHeaders = responseHeaders == null | 96 mResponseHeaders = responseHeaders == null |
92 ? new ArrayList<Pair<String, String>>() : responseHeaders; | 97 ? new ArrayList<Pair<String, String>>() : responseHeaders; |
93 mResponseAction = responseAction; | 98 mResponseAction = responseAction; |
94 } | 99 } |
95 } | 100 } |
96 | 101 |
97 // The Maps below are modified on both the client thread and the internal se rver thread, so | 102 // The Maps below are modified on both the client thread and the internal se rver thread, so |
98 // need to use a lock when accessing them. | 103 // need to use a lock when accessing them. |
99 private final Object mLock = new Object(); | 104 private final Object mLock = new Object(); |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
188 } | 193 } |
189 | 194 |
190 private static void setSecureInstance(TestWebServer instance) { | 195 private static void setSecureInstance(TestWebServer instance) { |
191 sSecureInstance = instance; | 196 sSecureInstance = instance; |
192 } | 197 } |
193 | 198 |
194 private static final int RESPONSE_STATUS_NORMAL = 0; | 199 private static final int RESPONSE_STATUS_NORMAL = 0; |
195 private static final int RESPONSE_STATUS_MOVED_TEMPORARILY = 1; | 200 private static final int RESPONSE_STATUS_MOVED_TEMPORARILY = 1; |
196 private static final int RESPONSE_STATUS_NOT_FOUND = 2; | 201 private static final int RESPONSE_STATUS_NOT_FOUND = 2; |
197 private static final int RESPONSE_STATUS_NO_CONTENT = 3; | 202 private static final int RESPONSE_STATUS_NO_CONTENT = 3; |
203 private static final int RESPONSE_STATUS_FOR_WEBSOCKET = 4; | |
198 | 204 |
199 private String setResponseInternal( | 205 private String setResponseInternal( |
200 String requestPath, byte[] responseData, | 206 String requestPath, byte[] responseData, |
201 List<Pair<String, String>> responseHeaders, Runnable responseAction, | 207 List<Pair<String, String>> responseHeaders, Runnable responseAction, |
202 int status) { | 208 int status) { |
203 final boolean isRedirect = (status == RESPONSE_STATUS_MOVED_TEMPORARILY) ; | 209 final boolean isRedirect = (status == RESPONSE_STATUS_MOVED_TEMPORARILY) ; |
204 final boolean isNotFound = (status == RESPONSE_STATUS_NOT_FOUND); | 210 final boolean isNotFound = (status == RESPONSE_STATUS_NOT_FOUND); |
205 final boolean isNoContent = (status == RESPONSE_STATUS_NO_CONTENT); | 211 final boolean isNoContent = (status == RESPONSE_STATUS_NO_CONTENT); |
212 final boolean forWebSocket = (status == RESPONSE_STATUS_FOR_WEBSOCKET); | |
206 | 213 |
207 synchronized (mLock) { | 214 synchronized (mLock) { |
208 mResponseMap.put(requestPath, new Response( | 215 mResponseMap.put( |
209 responseData, responseHeaders, isRedirect, isNotFound, isNoC ontent, | 216 requestPath, new Response(responseData, responseHeaders, isR edirect, isNotFound, |
210 responseAction)); | 217 isNoContent, forWebSocket, responseActi on)); |
211 mResponseCountMap.put(requestPath, Integer.valueOf(0)); | 218 mResponseCountMap.put(requestPath, Integer.valueOf(0)); |
212 mLastRequestMap.put(requestPath, null); | 219 mLastRequestMap.put(requestPath, null); |
213 } | 220 } |
214 return getResponseUrl(requestPath); | 221 return getResponseUrl(requestPath); |
215 } | 222 } |
216 | 223 |
217 /** | 224 /** |
218 * Gets the URL on the server under which a particular request path will be accessible. | 225 * Gets the URL on the server under which a particular request path will be accessible. |
219 * | 226 * |
220 * This only gets the URL, you still need to set the response if you intend to access it. | 227 * This only gets the URL, you still need to set the response if you intend to access it. |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
337 */ | 344 */ |
338 public String setResponseBase64( | 345 public String setResponseBase64( |
339 String requestPath, String base64EncodedResponse, | 346 String requestPath, String base64EncodedResponse, |
340 List<Pair<String, String>> responseHeaders) { | 347 List<Pair<String, String>> responseHeaders) { |
341 return setResponseInternal( | 348 return setResponseInternal( |
342 requestPath, Base64.decode(base64EncodedResponse, Base64.DEFAULT ), | 349 requestPath, Base64.decode(base64EncodedResponse, Base64.DEFAULT ), |
343 responseHeaders, null, RESPONSE_STATUS_NORMAL); | 350 responseHeaders, null, RESPONSE_STATUS_NORMAL); |
344 } | 351 } |
345 | 352 |
346 /** | 353 /** |
354 * Sets a response to a WebSocket handshake request. | |
355 * | |
356 * @param requestPath The path to respond to. | |
357 * @param responseHeaders Any additional headers that should be returned alo ng with the | |
358 * response (null is acceptable). | |
359 * @return The full URL including the path that should be requested to get t he expected | |
360 * response. | |
361 */ | |
362 public String setResponseForWebSocket( | |
363 String requestPath, List<Pair<String, String>> responseHeaders) { | |
364 if (responseHeaders == null) { | |
365 responseHeaders = new ArrayList<Pair<String, String>>(); | |
366 } | |
367 responseHeaders.add(Pair.create("Connection", "Upgrade")); | |
mef
2016/10/19 17:41:24
If responseHeaders passed in are not null, then th
yhirano
2016/10/20 04:21:07
Done.
| |
368 responseHeaders.add(Pair.create("Upgrade", "websocket")); | |
369 return setResponseInternal( | |
370 requestPath, "".getBytes(), responseHeaders, null, RESPONSE_STAT US_FOR_WEBSOCKET); | |
371 } | |
372 | |
373 /** | |
347 * Get the number of requests was made at this path since it was last set. | 374 * Get the number of requests was made at this path since it was last set. |
348 */ | 375 */ |
349 public int getRequestCount(String requestPath) { | 376 public int getRequestCount(String requestPath) { |
350 Integer count = null; | 377 Integer count = null; |
351 synchronized (mLock) { | 378 synchronized (mLock) { |
352 count = mResponseCountMap.get(requestPath); | 379 count = mResponseCountMap.get(requestPath); |
353 } | 380 } |
354 if (count == null) throw new IllegalArgumentException("Path not set: " + requestPath); | 381 if (count == null) throw new IllegalArgumentException("Path not set: " + requestPath); |
355 return count.intValue(); | 382 return count.intValue(); |
356 } | 383 } |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
474 } else if (response.mIsNoContent) { | 501 } else if (response.mIsNoContent) { |
475 httpResponse = createResponse(HttpStatus.SC_NO_CONTENT); | 502 httpResponse = createResponse(HttpStatus.SC_NO_CONTENT); |
476 httpResponse.setHeader("Content-Length", "0"); | 503 httpResponse.setHeader("Content-Length", "0"); |
477 servedResponseFor(path, request); | 504 servedResponseFor(path, request); |
478 } else if (response.mIsRedirect) { | 505 } else if (response.mIsRedirect) { |
479 httpResponse = createResponse(HttpStatus.SC_MOVED_TEMPORARILY); | 506 httpResponse = createResponse(HttpStatus.SC_MOVED_TEMPORARILY); |
480 for (Pair<String, String> header : response.mResponseHeaders) { | 507 for (Pair<String, String> header : response.mResponseHeaders) { |
481 httpResponse.addHeader(header.first, header.second); | 508 httpResponse.addHeader(header.first, header.second); |
482 } | 509 } |
483 servedResponseFor(path, request); | 510 servedResponseFor(path, request); |
511 } else if (response.mForWebSocket) { | |
512 Header[] keys = request.getHeaders("Sec-WebSocket-Key"); | |
513 try { | |
514 if (keys.length == 1) { | |
515 final String key = keys[0].getValue(); | |
516 httpResponse = createResponse(HttpStatus.SC_SWITCHING_PROTOC OLS); | |
517 for (Pair<String, String> header : response.mResponseHeaders ) { | |
518 httpResponse.addHeader(header.first, header.second); | |
519 } | |
520 httpResponse.addHeader("Sec-WebSocket-Accept", computeWebSoc ketAccept(key)); | |
521 servedResponseFor(path, request); | |
mef
2016/10/19 17:41:24
Should servedResponseFor(path, request); be also
yhirano
2016/10/20 04:21:07
Done.
| |
522 } else { | |
523 httpResponse = createResponse(HttpStatus.SC_NOT_FOUND); | |
524 } | |
525 } catch (NoSuchAlgorithmException e) { | |
526 httpResponse = createResponse(HttpStatus.SC_NOT_FOUND); | |
527 } | |
484 } else { | 528 } else { |
485 if (response.mResponseAction != null) response.mResponseAction.run() ; | 529 if (response.mResponseAction != null) response.mResponseAction.run() ; |
486 | 530 |
487 httpResponse = createResponse(HttpStatus.SC_OK); | 531 httpResponse = createResponse(HttpStatus.SC_OK); |
488 ByteArrayEntity entity = createEntity(response.mResponseData); | 532 ByteArrayEntity entity = createEntity(response.mResponseData); |
489 httpResponse.setEntity(entity); | 533 httpResponse.setEntity(entity); |
490 httpResponse.setHeader("Content-Length", "" + entity.getContentLengt h()); | 534 httpResponse.setHeader("Content-Length", "" + entity.getContentLengt h()); |
491 for (Pair<String, String> header : response.mResponseHeaders) { | 535 for (Pair<String, String> header : response.mResponseHeaders) { |
492 httpResponse.addHeader(header.first, header.second); | 536 httpResponse.addHeader(header.first, header.second); |
493 } | 537 } |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
545 | 589 |
546 /** | 590 /** |
547 * Create a string entity for the given content. | 591 * Create a string entity for the given content. |
548 */ | 592 */ |
549 private ByteArrayEntity createEntity(byte[] data) { | 593 private ByteArrayEntity createEntity(byte[] data) { |
550 ByteArrayEntity entity = new ByteArrayEntity(data); | 594 ByteArrayEntity entity = new ByteArrayEntity(data); |
551 entity.setContentType("text/html"); | 595 entity.setContentType("text/html"); |
552 return entity; | 596 return entity; |
553 } | 597 } |
554 | 598 |
599 /** | |
600 * Return a response for WebSocket handshake challenge. | |
601 */ | |
602 private static String computeWebSocketAccept(String keyString) throws NoSuch AlgorithmException { | |
603 byte[] key = keyString.getBytes(Charset.forName("US-ASCII")); | |
604 byte[] guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".getBytes(Charset.fo rName("US-ASCII")); | |
605 | |
606 MessageDigest md = MessageDigest.getInstance("SHA"); | |
607 md.update(key); | |
608 md.update(guid); | |
609 byte[] output = md.digest(); | |
610 return Base64.encodeToString(output, Base64.DEFAULT); | |
611 } | |
612 | |
555 private static class ServerThread extends Thread { | 613 private static class ServerThread extends Thread { |
556 private TestWebServer mServer; | 614 private TestWebServer mServer; |
557 private ServerSocket mSocket; | 615 private ServerSocket mSocket; |
558 private boolean mIsSsl; | 616 private boolean mIsSsl; |
559 private SSLContext mSslContext; | 617 private SSLContext mSslContext; |
560 | 618 |
561 private final Object mLock = new Object(); | 619 private final Object mLock = new Object(); |
562 private boolean mIsCancelled; | 620 private boolean mIsCancelled; |
563 private Socket mCurrentRequestSocket; | 621 private Socket mCurrentRequestSocket; |
564 | 622 |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
709 socket.close(); | 767 socket.close(); |
710 } catch (IOException ignored) { | 768 } catch (IOException ignored) { |
711 // safe to ignore | 769 // safe to ignore |
712 } | 770 } |
713 } | 771 } |
714 } | 772 } |
715 } | 773 } |
716 } | 774 } |
717 } | 775 } |
718 } | 776 } |
OLD | NEW |