1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.inputmethod.latin.network; 18 19 import android.text.TextUtils; 20 21 import com.android.inputmethod.annotations.UsedForTesting; 22 23 import java.io.IOException; 24 import java.net.HttpURLConnection; 25 import java.net.MalformedURLException; 26 import java.net.URL; 27 import java.util.HashMap; 28 import java.util.Map.Entry; 29 30 /** 31 * Builder for {@link HttpURLConnection}s. 32 * 33 * TODO: Remove @UsedForTesting after this is actually used. 34 */ 35 @UsedForTesting 36 public class HttpUrlConnectionBuilder { 37 private static final int DEFAULT_TIMEOUT_MILLIS = 5 * 1000; 38 39 /** 40 * Request header key for authentication. 41 */ 42 public static final String HTTP_HEADER_AUTHORIZATION = "Authorization"; 43 44 /** 45 * Request header key for cache control. 46 */ 47 public static final String KEY_CACHE_CONTROL = "Cache-Control"; 48 /** 49 * Request header value for cache control indicating no caching. 50 * @see #KEY_CACHE_CONTROL 51 */ 52 public static final String VALUE_NO_CACHE = "no-cache"; 53 54 /** 55 * Indicates that the request is unidirectional - upload-only. 56 * TODO: Remove @UsedForTesting after this is actually used. 57 */ 58 @UsedForTesting 59 public static final int MODE_UPLOAD_ONLY = 1; 60 /** 61 * Indicates that the request is unidirectional - download only. 62 * TODO: Remove @UsedForTesting after this is actually used. 63 */ 64 @UsedForTesting 65 public static final int MODE_DOWNLOAD_ONLY = 2; 66 /** 67 * Indicates that the request is bi-directional. 68 * TODO: Remove @UsedForTesting after this is actually used. 69 */ 70 @UsedForTesting 71 public static final int MODE_BI_DIRECTIONAL = 3; 72 73 private final HashMap<String, String> mHeaderMap = new HashMap<>(); 74 75 private URL mUrl; 76 private int mConnectTimeoutMillis = DEFAULT_TIMEOUT_MILLIS; 77 private int mReadTimeoutMillis = DEFAULT_TIMEOUT_MILLIS; 78 private int mContentLength = -1; 79 private boolean mUseCache; 80 private int mMode; 81 82 /** 83 * Sets the URL that'll be used for the request. 84 * This *must* be set before calling {@link #build()} 85 * 86 * TODO: Remove @UsedForTesting after this method is actually used. 87 */ 88 @UsedForTesting 89 public HttpUrlConnectionBuilder setUrl(String url) throws MalformedURLException { 90 if (TextUtils.isEmpty(url)) { 91 throw new IllegalArgumentException("URL must not be empty"); 92 } 93 mUrl = new URL(url); 94 return this; 95 } 96 97 /** 98 * Sets the connect timeout. Defaults to {@value #DEFAULT_TIMEOUT_MILLIS} milliseconds. 99 * 100 * TODO: Remove @UsedForTesting after this method is actually used. 101 */ 102 @UsedForTesting 103 public HttpUrlConnectionBuilder setConnectTimeout(int timeoutMillis) { 104 if (timeoutMillis < 0) { 105 throw new IllegalArgumentException("connect-timeout must be >= 0, but was " 106 + timeoutMillis); 107 } 108 mConnectTimeoutMillis = timeoutMillis; 109 return this; 110 } 111 112 /** 113 * Sets the read timeout. Defaults to {@value #DEFAULT_TIMEOUT_MILLIS} milliseconds. 114 * 115 * TODO: Remove @UsedForTesting after this method is actually used. 116 */ 117 @UsedForTesting 118 public HttpUrlConnectionBuilder setReadTimeout(int timeoutMillis) { 119 if (timeoutMillis < 0) { 120 throw new IllegalArgumentException("read-timeout must be >= 0, but was " 121 + timeoutMillis); 122 } 123 mReadTimeoutMillis = timeoutMillis; 124 return this; 125 } 126 127 /** 128 * Adds an entry to the request header. 129 * 130 * TODO: Remove @UsedForTesting after this method is actually used. 131 */ 132 @UsedForTesting 133 public HttpUrlConnectionBuilder addHeader(String key, String value) { 134 mHeaderMap.put(key, value); 135 return this; 136 } 137 138 /** 139 * Sets an authentication token. 140 * 141 * TODO: Remove @UsedForTesting after this method is actually used. 142 */ 143 @UsedForTesting 144 public HttpUrlConnectionBuilder setAuthToken(String value) { 145 mHeaderMap.put(HTTP_HEADER_AUTHORIZATION, value); 146 return this; 147 } 148 149 /** 150 * Sets the request to be executed such that the input is not buffered. 151 * This may be set when the request size is known beforehand. 152 * 153 * TODO: Remove @UsedForTesting after this method is actually used. 154 */ 155 @UsedForTesting 156 public HttpUrlConnectionBuilder setFixedLengthForStreaming(int length) { 157 mContentLength = length; 158 return this; 159 } 160 161 /** 162 * Indicates if the request can use cached responses or not. 163 * 164 * TODO: Remove @UsedForTesting after this method is actually used. 165 */ 166 @UsedForTesting 167 public HttpUrlConnectionBuilder setUseCache(boolean useCache) { 168 mUseCache = useCache; 169 return this; 170 } 171 172 /** 173 * The request mode. 174 * Sets the request mode to be one of: upload-only, download-only or bidirectional. 175 * 176 * @see #MODE_UPLOAD_ONLY 177 * @see #MODE_DOWNLOAD_ONLY 178 * @see #MODE_BI_DIRECTIONAL 179 * 180 * TODO: Remove @UsedForTesting after this method is actually used 181 */ 182 @UsedForTesting 183 public HttpUrlConnectionBuilder setMode(int mode) { 184 if (mode != MODE_UPLOAD_ONLY 185 && mode != MODE_DOWNLOAD_ONLY 186 && mode != MODE_BI_DIRECTIONAL) { 187 throw new IllegalArgumentException("Invalid mode specified:" + mode); 188 } 189 mMode = mode; 190 return this; 191 } 192 193 /** 194 * Builds the {@link HttpURLConnection} instance that can be used to execute the request. 195 * 196 * TODO: Remove @UsedForTesting after this method is actually used. 197 */ 198 @UsedForTesting 199 public HttpURLConnection build() throws IOException { 200 if (mUrl == null) { 201 throw new IllegalArgumentException("A URL must be specified!"); 202 } 203 final HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection(); 204 connection.setConnectTimeout(mConnectTimeoutMillis); 205 connection.setReadTimeout(mReadTimeoutMillis); 206 connection.setUseCaches(mUseCache); 207 switch (mMode) { 208 case MODE_UPLOAD_ONLY: 209 connection.setDoInput(true); 210 connection.setDoOutput(false); 211 break; 212 case MODE_DOWNLOAD_ONLY: 213 connection.setDoInput(false); 214 connection.setDoOutput(true); 215 break; 216 case MODE_BI_DIRECTIONAL: 217 connection.setDoInput(true); 218 connection.setDoOutput(true); 219 break; 220 } 221 for (final Entry<String, String> entry : mHeaderMap.entrySet()) { 222 connection.addRequestProperty(entry.getKey(), entry.getValue()); 223 } 224 if (mContentLength >= 0) { 225 connection.setFixedLengthStreamingMode(mContentLength); 226 } 227 return connection; 228 } 229 }