Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(115)

Side by Side Diff: components/cronet/android/java/src/org/chromium/net/impl/ChromiumUrlRequest.java

Issue 2339223002: Cronet API Refactoring (Closed)
Patch Set: Javadoc + rebase Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.net.impl;
6
7 import android.util.Log;
8
9 import org.chromium.base.annotations.CalledByNative;
10 import org.chromium.base.annotations.JNINamespace;
11 import org.chromium.base.annotations.SuppressFBWarnings;
12 import org.chromium.net.ChromiumUrlRequestError;
13 import org.chromium.net.ChromiumUrlRequestPriority;
14 import org.chromium.net.ChunkedWritableByteChannel;
15 import org.chromium.net.HttpUrlRequest;
16 import org.chromium.net.HttpUrlRequestListener;
17 import org.chromium.net.ResponseTooLargeException;
18
19 import java.io.IOException;
20 import java.net.MalformedURLException;
21 import java.net.SocketTimeoutException;
22 import java.net.URL;
23 import java.net.UnknownHostException;
24 import java.nio.ByteBuffer;
25 import java.nio.channels.ReadableByteChannel;
26 import java.nio.channels.WritableByteChannel;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Map.Entry;
32
33 /**
34 * Network request using the native http stack implementation.
35 * @deprecated Use {@link CronetUrlRequest} instead.
36 */
37 @JNINamespace("cronet")
38 @Deprecated
39 public class ChromiumUrlRequest implements HttpUrlRequest {
40 /**
41 * Native adapter object, owned by UrlRequest.
42 */
43 private long mUrlRequestAdapter;
44 private final ChromiumUrlRequestContext mRequestContext;
45 private final String mUrl;
46 private final int mPriority;
47 private final Map<String, String> mHeaders;
48 private final WritableByteChannel mSink;
49 private Map<String, String> mAdditionalHeaders;
50 private String mUploadContentType;
51 private String mMethod;
52 private byte[] mUploadData;
53 private ReadableByteChannel mUploadChannel;
54 private boolean mChunkedUpload;
55 private IOException mSinkException;
56 private volatile boolean mStarted;
57 private volatile boolean mCanceled;
58 private volatile boolean mFinished;
59 private boolean mHeadersAvailable;
60 private long mUploadContentLength;
61 private final HttpUrlRequestListener mListener;
62 private boolean mBufferFullResponse;
63 private long mOffset;
64 private long mContentLengthLimit;
65 private boolean mCancelIfContentLengthOverLimit;
66 private boolean mContentLengthOverLimit;
67 private boolean mSkippingToOffset;
68 private long mSize;
69
70 // Indicates whether redirects have been disabled.
71 private boolean mDisableRedirects;
72
73 // Http status code. Default to 0. Populated in onResponseStarted().
74 private int mHttpStatusCode = 0;
75
76 // Http status text. Default to null. Populated in onResponseStarted().
77 private String mHttpStatusText;
78
79 // Content type. Default to null. Populated in onResponseStarted().
80 private String mContentType;
81
82 // Compressed content length as reported by the server. Populated in onRespo nseStarted().
83 private long mContentLength;
84
85 // Native error code. Default to no error. Populated in onRequestComplete().
86 private int mErrorCode = ChromiumUrlRequestError.SUCCESS;
87
88 // Native error string. Default to null. Populated in onRequestComplete().
89 private String mErrorString;
90
91 // Protects access of mUrlRequestAdapter, mStarted, mCanceled, and mFinished .
92 private final Object mLock = new Object();
93
94 public ChromiumUrlRequest(ChromiumUrlRequestContext requestContext, String u rl, int priority,
95 Map<String, String> headers, HttpUrlRequestListener listener) {
96 this(requestContext, url, priority, headers, new ChunkedWritableByteChan nel(), listener);
97 mBufferFullResponse = true;
98 }
99
100 /**
101 * Constructor.
102 *
103 * @param requestContext The context.
104 * @param url The URL.
105 * @param priority Request priority, e.g. {@link #REQUEST_PRIORITY_MEDIUM}.
106 * @param headers HTTP headers.
107 * @param sink The output channel into which downloaded content will be
108 * written.
109 */
110 public ChromiumUrlRequest(ChromiumUrlRequestContext requestContext, String u rl, int priority,
111 Map<String, String> headers, WritableByteChannel sink,
112 HttpUrlRequestListener listener) {
113 if (requestContext == null) {
114 throw new NullPointerException("Context is required");
115 }
116 if (url == null) {
117 throw new NullPointerException("URL is required");
118 }
119 mRequestContext = requestContext;
120 mUrl = url;
121 mPriority = convertRequestPriority(priority);
122 mHeaders = headers;
123 mSink = sink;
124 mUrlRequestAdapter = nativeCreateRequestAdapter(
125 mRequestContext.getUrlRequestContextAdapter(), mUrl, mPriority);
126 mListener = listener;
127 }
128
129 @Override
130 public void setOffset(long offset) {
131 mOffset = offset;
132 if (offset != 0) {
133 addHeader("Range", "bytes=" + offset + "-");
134 }
135 }
136
137 /**
138 * The compressed content length as reported by the server. May be -1 if
139 * the server did not provide a length. Some servers may also report the
140 * wrong number. Since this is the compressed content length, and only
141 * uncompressed content is returned by the consumer, the consumer should
142 * not rely on this value.
143 */
144 @Override
145 public long getContentLength() {
146 return mContentLength;
147 }
148
149 @Override
150 public void setContentLengthLimit(long limit, boolean cancelEarly) {
151 mContentLengthLimit = limit;
152 mCancelIfContentLengthOverLimit = cancelEarly;
153 }
154
155 @Override
156 public int getHttpStatusCode() {
157 // TODO(mef): Investigate the following:
158 // If we have been able to successfully resume a previously interrupted
159 // download, the status code will be 206, not 200. Since the rest of the
160 // application is expecting 200 to indicate success, we need to fake it.
161 if (mHttpStatusCode == 206) {
162 return 200;
163 }
164 return mHttpStatusCode;
165 }
166
167 @Override
168 public String getHttpStatusText() {
169 return mHttpStatusText;
170 }
171
172 /**
173 * Returns an exception if any, or null if the request has not completed or
174 * completed successfully.
175 */
176 @Override
177 public IOException getException() {
178 if (mSinkException != null) {
179 return mSinkException;
180 }
181
182 switch (mErrorCode) {
183 case ChromiumUrlRequestError.SUCCESS:
184 if (mContentLengthOverLimit) {
185 return new ResponseTooLargeException();
186 }
187 return null;
188 case ChromiumUrlRequestError.UNKNOWN:
189 return new IOException(mErrorString);
190 case ChromiumUrlRequestError.MALFORMED_URL:
191 return new MalformedURLException("Malformed URL: " + mUrl);
192 case ChromiumUrlRequestError.CONNECTION_TIMED_OUT:
193 return new SocketTimeoutException("Connection timed out");
194 case ChromiumUrlRequestError.UNKNOWN_HOST:
195 String host;
196 try {
197 host = new URL(mUrl).getHost();
198 } catch (MalformedURLException e) {
199 host = mUrl;
200 }
201 return new UnknownHostException("Unknown host: " + host);
202 case ChromiumUrlRequestError.TOO_MANY_REDIRECTS:
203 return new IOException("Request failed because there were too "
204 + "many redirects or redirects have been disabled");
205 default:
206 throw new IllegalStateException("Unrecognized error code: " + mE rrorCode);
207 }
208 }
209
210 @Override
211 public ByteBuffer getByteBuffer() {
212 return ((ChunkedWritableByteChannel) getSink()).getByteBuffer();
213 }
214
215 @Override
216 public byte[] getResponseAsBytes() {
217 return ((ChunkedWritableByteChannel) getSink()).getBytes();
218 }
219
220 /**
221 * Adds a request header. Must be done before request has started.
222 */
223 public void addHeader(String header, String value) {
224 synchronized (mLock) {
225 validateNotStarted();
226 if (mAdditionalHeaders == null) {
227 mAdditionalHeaders = new HashMap<String, String>();
228 }
229 mAdditionalHeaders.put(header, value);
230 }
231 }
232
233 /**
234 * Sets data to upload as part of a POST or PUT request.
235 *
236 * @param contentType MIME type of the upload content or null if this is not
237 * an upload.
238 * @param data The content that needs to be uploaded.
239 */
240 @Override
241 @SuppressFBWarnings("EI_EXPOSE_REP2")
242 public void setUploadData(String contentType, byte[] data) {
243 synchronized (mLock) {
244 validateNotStarted();
245 validateContentType(contentType);
246 mUploadContentType = contentType;
247 mUploadData = data;
248 mUploadChannel = null;
249 mChunkedUpload = false;
250 }
251 }
252
253 /**
254 * Sets a readable byte channel to upload as part of a POST or PUT request.
255 *
256 * @param contentType MIME type of the upload content or null if this is not
257 * an upload request.
258 * @param channel The channel to read to read upload data from if this is an
259 * upload request.
260 * @param contentLength The length of data to upload.
261 */
262 @Override
263 public void setUploadChannel(
264 String contentType, ReadableByteChannel channel, long contentLength) {
265 synchronized (mLock) {
266 validateNotStarted();
267 validateContentType(contentType);
268 mUploadContentType = contentType;
269 mUploadChannel = channel;
270 mUploadContentLength = contentLength;
271 mUploadData = null;
272 mChunkedUpload = false;
273 }
274 }
275
276 /**
277 * Sets this request up for chunked uploading. To upload data call
278 * {@link #appendChunk(ByteBuffer, boolean)} after {@link #start()}.
279 *
280 * @param contentType MIME type of the post content or null if this is not a
281 * POST request.
282 */
283 public void setChunkedUpload(String contentType) {
284 synchronized (mLock) {
285 validateNotStarted();
286 validateContentType(contentType);
287 mUploadContentType = contentType;
288 mChunkedUpload = true;
289 mUploadData = null;
290 mUploadChannel = null;
291 }
292 }
293
294 /**
295 * Uploads a new chunk. Must have called {@link #setChunkedUpload(String)}
296 * and {@link #start()}.
297 *
298 * @param chunk The data, which will not be modified. Its current position.
299 * must be zero. The last chunk can be empty, but all other
300 * chunks must be non-empty.
301 * @param isLastChunk Whether this chunk is the last one.
302 */
303 public void appendChunk(ByteBuffer chunk, boolean isLastChunk) throws IOExce ption {
304 if (!isLastChunk && !chunk.hasRemaining()) {
305 throw new IllegalArgumentException("Attempted to write empty buffer. ");
306 }
307 if (chunk.position() != 0) {
308 throw new IllegalArgumentException("The position must be zero.");
309 }
310 synchronized (mLock) {
311 if (!mStarted) {
312 throw new IllegalStateException("Request not yet started.");
313 }
314 if (!mChunkedUpload) {
315 throw new IllegalStateException("Request not set for chunked upl oadind.");
316 }
317 if (mUrlRequestAdapter == 0) {
318 throw new IOException("Native peer destroyed.");
319 }
320 nativeAppendChunk(mUrlRequestAdapter, chunk, chunk.limit(), isLastCh unk);
321 }
322 }
323
324 @Override
325 public void setHttpMethod(String method) {
326 validateNotStarted();
327 mMethod = method;
328 }
329
330 @Override
331 public void disableRedirects() {
332 synchronized (mLock) {
333 validateNotStarted();
334 validateNativeAdapterNotDestroyed();
335 mDisableRedirects = true;
336 nativeDisableRedirects(mUrlRequestAdapter);
337 }
338 }
339
340 public WritableByteChannel getSink() {
341 return mSink;
342 }
343
344 @Override
345 public void start() {
346 synchronized (mLock) {
347 if (mCanceled) {
348 return;
349 }
350
351 validateNotStarted();
352 validateNativeAdapterNotDestroyed();
353
354 mStarted = true;
355
356 if (mHeaders != null && !mHeaders.isEmpty()) {
357 for (Entry<String, String> entry : mHeaders.entrySet()) {
358 nativeAddHeader(mUrlRequestAdapter, entry.getKey(), entry.ge tValue());
359 }
360 }
361
362 if (mAdditionalHeaders != null) {
363 for (Entry<String, String> entry : mAdditionalHeaders.entrySet() ) {
364 nativeAddHeader(mUrlRequestAdapter, entry.getKey(), entry.ge tValue());
365 }
366 }
367
368 if (mUploadData != null && mUploadData.length > 0) {
369 nativeSetUploadData(mUrlRequestAdapter, mUploadContentType, mUpl oadData);
370 } else if (mUploadChannel != null) {
371 nativeSetUploadChannel(
372 mUrlRequestAdapter, mUploadContentType, mUploadContentLe ngth);
373 } else if (mChunkedUpload) {
374 nativeEnableChunkedUpload(mUrlRequestAdapter, mUploadContentType );
375 }
376
377 // Note: The above functions to set the upload body also set the
378 // method to POST, behind the scenes, so if mMethod is null but
379 // there's an upload body, the method will default to POST.
380 if (mMethod != null) {
381 nativeSetMethod(mUrlRequestAdapter, mMethod);
382 }
383
384 nativeStart(mUrlRequestAdapter);
385 }
386 }
387
388 @Override
389 public void cancel() {
390 synchronized (mLock) {
391 if (mCanceled) {
392 return;
393 }
394
395 mCanceled = true;
396
397 if (mUrlRequestAdapter != 0) {
398 nativeCancel(mUrlRequestAdapter);
399 }
400 }
401 }
402
403 @Override
404 public boolean isCanceled() {
405 synchronized (mLock) {
406 return mCanceled;
407 }
408 }
409
410 @Override
411 public String getNegotiatedProtocol() {
412 synchronized (mLock) {
413 validateNativeAdapterNotDestroyed();
414 validateHeadersAvailable();
415 return nativeGetNegotiatedProtocol(mUrlRequestAdapter);
416 }
417 }
418
419 @Override
420 public boolean wasCached() {
421 synchronized (mLock) {
422 validateNativeAdapterNotDestroyed();
423 validateHeadersAvailable();
424 return nativeGetWasCached(mUrlRequestAdapter);
425 }
426 }
427
428 @Override
429 public String getContentType() {
430 return mContentType;
431 }
432
433 @Override
434 public String getHeader(String name) {
435 synchronized (mLock) {
436 validateNativeAdapterNotDestroyed();
437 validateHeadersAvailable();
438 return nativeGetHeader(mUrlRequestAdapter, name);
439 }
440 }
441
442 // All response headers.
443 @Override
444 public Map<String, List<String>> getAllHeaders() {
445 synchronized (mLock) {
446 validateNativeAdapterNotDestroyed();
447 validateHeadersAvailable();
448 ResponseHeadersMap result = new ResponseHeadersMap();
449 nativeGetAllHeaders(mUrlRequestAdapter, result);
450 return result;
451 }
452 }
453
454 @Override
455 public String getUrl() {
456 return mUrl;
457 }
458
459 private static int convertRequestPriority(int priority) {
460 switch (priority) {
461 case REQUEST_PRIORITY_IDLE:
462 return ChromiumUrlRequestPriority.IDLE;
463 case REQUEST_PRIORITY_LOWEST:
464 return ChromiumUrlRequestPriority.LOWEST;
465 case REQUEST_PRIORITY_LOW:
466 return ChromiumUrlRequestPriority.LOW;
467 case REQUEST_PRIORITY_MEDIUM:
468 return ChromiumUrlRequestPriority.MEDIUM;
469 case REQUEST_PRIORITY_HIGHEST:
470 return ChromiumUrlRequestPriority.HIGHEST;
471 default:
472 return ChromiumUrlRequestPriority.MEDIUM;
473 }
474 }
475
476 private void onContentLengthOverLimit() {
477 mContentLengthOverLimit = true;
478 cancel();
479 }
480
481 /**
482 * A callback invoked when the response has been fully consumed.
483 */
484 private void onRequestComplete() {
485 mErrorCode = nativeGetErrorCode(mUrlRequestAdapter);
486 mErrorString = nativeGetErrorString(mUrlRequestAdapter);
487 // When there is an error or redirects have been disabled,
488 // onResponseStarted is often not invoked.
489 // Populate status code and status text if that's the case.
490 // Note that besides redirects, these two fields may be set on the
491 // request for AUTH and CERT requests.
492 if (mErrorCode != ChromiumUrlRequestError.SUCCESS) {
493 mHttpStatusCode = nativeGetHttpStatusCode(mUrlRequestAdapter);
494 mHttpStatusText = nativeGetHttpStatusText(mUrlRequestAdapter);
495 }
496 mListener.onRequestComplete(this);
497 }
498
499 private void validateNativeAdapterNotDestroyed() {
500 if (mUrlRequestAdapter == 0) {
501 throw new IllegalStateException("Adapter has been destroyed");
502 }
503 }
504
505 private void validateNotStarted() {
506 if (mStarted) {
507 throw new IllegalStateException("Request already started");
508 }
509 }
510
511 private void validateHeadersAvailable() {
512 if (!mHeadersAvailable) {
513 throw new IllegalStateException("Response headers not available");
514 }
515 }
516
517 private void validateContentType(String contentType) {
518 if (contentType == null) {
519 throw new NullPointerException("contentType is required");
520 }
521 }
522
523 // Private methods called by native library.
524
525 /**
526 * If @CalledByNative method throws an exception, request gets canceled
527 * and exception could be retrieved from request using getException().
528 */
529 private void onCalledByNativeException(Exception e) {
530 mSinkException = new IOException("CalledByNative method has thrown an ex ception", e);
531 Log.e(ChromiumUrlRequestContext.LOG_TAG, "Exception in CalledByNative me thod", e);
532 try {
533 cancel();
534 } catch (Exception cancel_exception) {
535 Log.e(ChromiumUrlRequestContext.LOG_TAG, "Exception trying to cancel request",
536 cancel_exception);
537 }
538 }
539
540 /**
541 * A callback invoked when the first chunk of the response has arrived.
542 */
543 @CalledByNative
544 private void onResponseStarted() {
545 try {
546 mHttpStatusCode = nativeGetHttpStatusCode(mUrlRequestAdapter);
547 mHttpStatusText = nativeGetHttpStatusText(mUrlRequestAdapter);
548 mContentType = nativeGetContentType(mUrlRequestAdapter);
549 mContentLength = nativeGetContentLength(mUrlRequestAdapter);
550 mHeadersAvailable = true;
551
552 if (mContentLengthLimit > 0 && mContentLength > mContentLengthLimit
553 && mCancelIfContentLengthOverLimit) {
554 onContentLengthOverLimit();
555 return;
556 }
557
558 if (mBufferFullResponse && mContentLength != -1 && !mContentLengthOv erLimit) {
559 ((ChunkedWritableByteChannel) getSink()).setCapacity((int) mCont entLength);
560 }
561
562 if (mOffset != 0) {
563 // The server may ignore the request for a byte range, in which
564 // case status code will be 200, instead of 206. Note that we
565 // cannot call getHttpStatusCode as it rewrites 206 into 200.
566 if (mHttpStatusCode == 200) {
567 // TODO(mef): Revisit this logic.
568 if (mContentLength != -1) {
569 mContentLength -= mOffset;
570 }
571 mSkippingToOffset = true;
572 } else {
573 mSize = mOffset;
574 }
575 }
576 mListener.onResponseStarted(this);
577 } catch (Exception e) {
578 onCalledByNativeException(e);
579 }
580 }
581
582 /**
583 * Consumes a portion of the response.
584 *
585 * @param byteBuffer The ByteBuffer to append. Must be a direct buffer, and
586 * no references to it may be retained after the method ends, as
587 * it wraps code managed on the native heap.
588 */
589 @CalledByNative
590 private void onBytesRead(ByteBuffer buffer) {
591 try {
592 if (mContentLengthOverLimit) {
593 return;
594 }
595
596 int size = buffer.remaining();
597 mSize += size;
598 if (mSkippingToOffset) {
599 if (mSize <= mOffset) {
600 return;
601 } else {
602 mSkippingToOffset = false;
603 buffer.position((int) (mOffset - (mSize - size)));
604 }
605 }
606
607 boolean contentLengthOverLimit =
608 (mContentLengthLimit != 0 && mSize > mContentLengthLimit);
609 if (contentLengthOverLimit) {
610 buffer.limit(size - (int) (mSize - mContentLengthLimit));
611 }
612
613 while (buffer.hasRemaining()) {
614 mSink.write(buffer);
615 }
616 if (contentLengthOverLimit) {
617 onContentLengthOverLimit();
618 }
619 } catch (Exception e) {
620 onCalledByNativeException(e);
621 }
622 }
623
624 /**
625 * Notifies the listener, releases native data structures.
626 */
627 @SuppressWarnings("unused")
628 @CalledByNative
629 private void finish() {
630 try {
631 synchronized (mLock) {
632 if (mDisableRedirects) {
633 mHeadersAvailable = true;
634 }
635 mFinished = true;
636
637 if (mUrlRequestAdapter == 0) {
638 return;
639 }
640 try {
641 mSink.close();
642 } catch (IOException e) {
643 // Ignore
644 }
645 try {
646 if (mUploadChannel != null && mUploadChannel.isOpen()) {
647 mUploadChannel.close();
648 }
649 } catch (IOException e) {
650 // Ignore
651 }
652 onRequestComplete();
653 nativeDestroyRequestAdapter(mUrlRequestAdapter);
654 mUrlRequestAdapter = 0;
655 }
656 } catch (Exception e) {
657 mSinkException = new IOException("Exception in finish", e);
658 }
659 }
660
661 /**
662 * Appends header |name| with value |value| to |headersMap|.
663 */
664 @SuppressWarnings("unused")
665 @CalledByNative
666 private void onAppendResponseHeader(ResponseHeadersMap headersMap, String na me, String value) {
667 try {
668 if (!headersMap.containsKey(name)) {
669 headersMap.put(name, new ArrayList<String>());
670 }
671 headersMap.get(name).add(value);
672 } catch (Exception e) {
673 onCalledByNativeException(e);
674 }
675 }
676
677 /**
678 * Reads a sequence of bytes from upload channel into the given buffer.
679 * @param dest The buffer into which bytes are to be transferred.
680 * @return Returns number of bytes read (could be 0) or -1 and closes
681 * the channel if error occured.
682 */
683 @SuppressWarnings("unused")
684 @CalledByNative
685 private int readFromUploadChannel(ByteBuffer dest) {
686 try {
687 if (mUploadChannel == null || !mUploadChannel.isOpen()) return -1;
688 int result = mUploadChannel.read(dest);
689 if (result < 0) {
690 mUploadChannel.close();
691 return 0;
692 }
693 return result;
694 } catch (Exception e) {
695 onCalledByNativeException(e);
696 }
697 return -1;
698 }
699
700 // Native methods are implemented in chromium_url_request.cc.
701
702 private native long nativeCreateRequestAdapter(
703 long urlRequestContextAdapter, String url, int priority);
704
705 private native void nativeAddHeader(long urlRequestAdapter, String name, Str ing value);
706
707 private native void nativeSetMethod(long urlRequestAdapter, String method);
708
709 private native void nativeSetUploadData(
710 long urlRequestAdapter, String contentType, byte[] content);
711
712 private native void nativeSetUploadChannel(
713 long urlRequestAdapter, String contentType, long contentLength);
714
715 private native void nativeEnableChunkedUpload(long urlRequestAdapter, String contentType);
716
717 private native void nativeDisableRedirects(long urlRequestAdapter);
718
719 private native void nativeAppendChunk(
720 long urlRequestAdapter, ByteBuffer chunk, int chunkSize, boolean isL astChunk);
721
722 private native void nativeStart(long urlRequestAdapter);
723
724 private native void nativeCancel(long urlRequestAdapter);
725
726 private native void nativeDestroyRequestAdapter(long urlRequestAdapter);
727
728 private native int nativeGetErrorCode(long urlRequestAdapter);
729
730 private native int nativeGetHttpStatusCode(long urlRequestAdapter);
731
732 private native String nativeGetHttpStatusText(long urlRequestAdapter);
733
734 private native String nativeGetErrorString(long urlRequestAdapter);
735
736 private native String nativeGetContentType(long urlRequestAdapter);
737
738 private native long nativeGetContentLength(long urlRequestAdapter);
739
740 private native String nativeGetHeader(long urlRequestAdapter, String name);
741
742 private native void nativeGetAllHeaders(long urlRequestAdapter, ResponseHead ersMap headers);
743
744 private native String nativeGetNegotiatedProtocol(long urlRequestAdapter);
745
746 private native boolean nativeGetWasCached(long urlRequestAdapter);
747
748 // Explicit class to work around JNI-generator generics confusion.
749 private static class ResponseHeadersMap extends HashMap<String, List<String> > {}
750 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698