1 // Copyright 2016 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 package org.chromium.net.impl; 5 6 import android.annotation.SuppressLint; 7 import android.os.Build; 8 import android.util.Log; 9 import android.util.Pair; 10 11 import org.chromium.net.CronetEngine; 12 import org.chromium.net.ExperimentalUrlRequest; 13 import org.chromium.net.RequestFinishedInfo; 14 import org.chromium.net.UploadDataProvider; 15 import org.chromium.net.UrlRequest; 16 17 import java.util.ArrayList; 18 import java.util.Collection; 19 import java.util.concurrent.Executor; 20 21 /** Implements {@link org.chromium.net.ExperimentalUrlRequest.Builder}. */ 22 public class UrlRequestBuilderImpl extends ExperimentalUrlRequest.Builder { 23 private static final String ACCEPT_ENCODING = "Accept-Encoding"; 24 private static final String TAG = UrlRequestBuilderImpl.class.getSimpleName(); 25 26 // All fields are temporary storage of ExperimentalUrlRequest configuration to be 27 // copied to built ExperimentalUrlRequest. 28 29 // CronetEngineBase to execute request. 30 private final CronetEngineBase mCronetEngine; 31 // URL to request. 32 private final String mUrl; 33 // Callback to receive progress callbacks. 34 private final UrlRequest.Callback mCallback; 35 // Executor to invoke callback on. 36 private final Executor mExecutor; 37 // HTTP method (e.g. GET, POST etc). 38 private String mMethod; 39 40 // List of request headers, stored as header field name and value pairs. 41 private final ArrayList<Pair<String, String>> mRequestHeaders = new ArrayList<>(); 42 // Disable the cache for just this request. 43 private boolean mDisableCache; 44 // Disable connection migration for just this request. 45 private boolean mDisableConnectionMigration; 46 // Priority of request. Default is medium. 47 @CronetEngineBase.RequestPriority private int mPriority = REQUEST_PRIORITY_MEDIUM; 48 // Request reporting annotations. Avoid extra object creation if no annotations added. 49 private Collection<Object> mRequestAnnotations; 50 // If request is an upload, this provides the request body data. 51 private UploadDataProvider mUploadDataProvider; 52 // Executor to call upload data provider back on. 53 private Executor mUploadDataProviderExecutor; 54 private boolean mAllowDirectExecutor; 55 private boolean mTrafficStatsTagSet; 56 private int mTrafficStatsTag; 57 private boolean mTrafficStatsUidSet; 58 private int mTrafficStatsUid; 59 private RequestFinishedInfo.Listener mRequestFinishedListener; 60 private long mNetworkHandle = CronetEngineBase.DEFAULT_NETWORK_HANDLE; 61 // Idempotency of the request. 62 @CronetEngineBase.Idempotency private int mIdempotency = DEFAULT_IDEMPOTENCY; 63 64 /** 65 * Creates a builder for {@link UrlRequest} objects. All callbacks for 66 * generated {@link UrlRequest} objects will be invoked on 67 * {@code executor}'s thread. {@code executor} must not run tasks on the 68 * current thread to prevent blocking networking operations and causing 69 * exceptions during shutdown. 70 * 71 * @param url URL for the generated requests. 72 * @param callback callback object that gets invoked on different events. 73 * @param executor {@link Executor} on which all callbacks will be invoked. 74 * @param cronetEngine {@link CronetEngine} used to execute this request. 75 */ UrlRequestBuilderImpl( String url, UrlRequest.Callback callback, Executor executor, CronetEngineBase cronetEngine)76 UrlRequestBuilderImpl( 77 String url, 78 UrlRequest.Callback callback, 79 Executor executor, 80 CronetEngineBase cronetEngine) { 81 super(); 82 if (url == null) { 83 throw new NullPointerException("URL is required."); 84 } 85 if (callback == null) { 86 throw new NullPointerException("Callback is required."); 87 } 88 if (executor == null) { 89 throw new NullPointerException("Executor is required."); 90 } 91 if (cronetEngine == null) { 92 throw new NullPointerException("CronetEngine is required."); 93 } 94 mUrl = url; 95 mCallback = callback; 96 mExecutor = executor; 97 mCronetEngine = cronetEngine; 98 } 99 100 @Override setHttpMethod(String method)101 public ExperimentalUrlRequest.Builder setHttpMethod(String method) { 102 if (method == null) { 103 throw new NullPointerException("Method is required."); 104 } 105 mMethod = method; 106 return this; 107 } 108 109 @Override addHeader(String header, String value)110 public UrlRequestBuilderImpl addHeader(String header, String value) { 111 if (header == null) { 112 throw new NullPointerException("Invalid header name."); 113 } 114 if (value == null) { 115 throw new NullPointerException("Invalid header value."); 116 } 117 if (ACCEPT_ENCODING.equalsIgnoreCase(header)) { 118 Log.w( 119 TAG, 120 "It's not necessary to set Accept-Encoding on requests - cronet will do" 121 + " this automatically for you, and setting it yourself has no " 122 + "effect. See https://crbug.com/581399 for details.", 123 new Exception()); 124 return this; 125 } 126 mRequestHeaders.add(Pair.create(header, value)); 127 return this; 128 } 129 130 @Override disableCache()131 public UrlRequestBuilderImpl disableCache() { 132 mDisableCache = true; 133 return this; 134 } 135 136 @Override disableConnectionMigration()137 public UrlRequestBuilderImpl disableConnectionMigration() { 138 mDisableConnectionMigration = true; 139 return this; 140 } 141 142 @Override setPriority(@ronetEngineBase.RequestPriority int priority)143 public UrlRequestBuilderImpl setPriority(@CronetEngineBase.RequestPriority int priority) { 144 mPriority = priority; 145 return this; 146 } 147 148 @Override setIdempotency(@ronetEngineBase.Idempotency int idempotency)149 public UrlRequestBuilderImpl setIdempotency(@CronetEngineBase.Idempotency int idempotency) { 150 mIdempotency = idempotency; 151 return this; 152 } 153 154 @Override setUploadDataProvider( UploadDataProvider uploadDataProvider, Executor executor)155 public UrlRequestBuilderImpl setUploadDataProvider( 156 UploadDataProvider uploadDataProvider, Executor executor) { 157 if (uploadDataProvider == null) { 158 throw new NullPointerException("Invalid UploadDataProvider."); 159 } 160 if (executor == null) { 161 throw new NullPointerException("Invalid UploadDataProvider Executor."); 162 } 163 if (mMethod == null) { 164 mMethod = "POST"; 165 } 166 mUploadDataProvider = uploadDataProvider; 167 mUploadDataProviderExecutor = executor; 168 return this; 169 } 170 171 @Override allowDirectExecutor()172 public UrlRequestBuilderImpl allowDirectExecutor() { 173 mAllowDirectExecutor = true; 174 return this; 175 } 176 177 @Override addRequestAnnotation(Object annotation)178 public UrlRequestBuilderImpl addRequestAnnotation(Object annotation) { 179 if (annotation == null) { 180 throw new NullPointerException("Invalid metrics annotation."); 181 } 182 if (mRequestAnnotations == null) { 183 mRequestAnnotations = new ArrayList<>(); 184 } 185 mRequestAnnotations.add(annotation); 186 return this; 187 } 188 189 @Override setTrafficStatsTag(int tag)190 public UrlRequestBuilderImpl setTrafficStatsTag(int tag) { 191 mTrafficStatsTagSet = true; 192 mTrafficStatsTag = tag; 193 return this; 194 } 195 196 @Override setTrafficStatsUid(int uid)197 public UrlRequestBuilderImpl setTrafficStatsUid(int uid) { 198 mTrafficStatsUidSet = true; 199 mTrafficStatsUid = uid; 200 return this; 201 } 202 203 @Override setRequestFinishedListener(RequestFinishedInfo.Listener listener)204 public UrlRequestBuilderImpl setRequestFinishedListener(RequestFinishedInfo.Listener listener) { 205 mRequestFinishedListener = listener; 206 return this; 207 } 208 209 @Override bindToNetwork(long networkHandle)210 public UrlRequestBuilderImpl bindToNetwork(long networkHandle) { 211 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 212 throw new UnsupportedOperationException( 213 "The multi-network API is available starting from Android Marshmallow"); 214 } 215 mNetworkHandle = networkHandle; 216 return this; 217 } 218 219 @Override build()220 public UrlRequestBase build() { 221 @SuppressLint("WrongConstant") // TODO(jbudorick): Remove this after rolling to the N SDK. 222 final UrlRequestBase request = 223 mCronetEngine.createRequest( 224 mUrl, 225 mCallback, 226 mExecutor, 227 mPriority, 228 mRequestAnnotations, 229 mDisableCache, 230 mDisableConnectionMigration, 231 mAllowDirectExecutor, 232 mTrafficStatsTagSet, 233 mTrafficStatsTag, 234 mTrafficStatsUidSet, 235 mTrafficStatsUid, 236 mRequestFinishedListener, 237 mIdempotency, 238 mNetworkHandle); 239 if (mMethod != null) { 240 request.setHttpMethod(mMethod); 241 } 242 for (Pair<String, String> header : mRequestHeaders) { 243 request.addHeader(header.first, header.second); 244 } 245 if (mUploadDataProvider != null) { 246 request.setUploadDataProvider(mUploadDataProvider, mUploadDataProviderExecutor); 247 } 248 return request; 249 } 250 } 251