Chromium Code Reviews| 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 |